Comment !! ~ (pas tilde / bang bang tilde) modifie-t-il le résultat d'un appel à la méthode Array 'contient / inclus'?

95

Si vous lisez les commentaires sur la inArraypage jQuery ici , il y a une déclaration intéressante:

!!~jQuery.inArray(elm, arr) 

Maintenant, je crois qu'un point d'exclamation double convertira le résultat en type boolean, avec la valeur de true. Ce que je ne comprends pas, c'est à quoi sert l' ~opérateur tilde ( ) dans tout cela?

var arr = ["one", "two", "three"];
if (jQuery.inArray("one", arr) > -1) { alert("Found"); }

Refactorisation de l' ifinstruction:

if (!!~jQuery.inArray("one", arr)) { alert("Found"); }

Panne:

jQuery.inArray("one", arr)     // 0
~jQuery.inArray("one", arr)    // -1 (why?)
!~jQuery.inArray("one", arr)   // false
!!~jQuery.inArray("one", arr)  // true

J'ai aussi remarqué que si je mets le tilde devant, le résultat est -2.

~!!~jQuery.inArray("one", arr) // -2

Je ne comprends pas le but du tilde ici. Quelqu'un peut-il l'expliquer ou me diriger vers une ressource?

user717236
la source
50
Quiconque écrit un code comme celui-là doit s'éloigner du clavier.
Kirk Woll
12
@KirkWoll: Pourquoi? ~jQuery.inArray()est en fait très utile - peut-être même une très bonne raison pour laquelle les fonctions de recherche retournent en -1cas d'échec (la seule valeur dont le complément à deux est faux). Une fois que vous avez vu et compris le truc, je pense qu'il est encore plus lisible que != -1.
Amadan
9
@Amadan - non. Tout simplement pas. Sérieusement, je ne peux pas croire que vous vous défendez !!~pour quoi que ce soit .
Kirk Woll
24
Le problème est que c'est juste ça: un "truc". La principale différence entre if (x != -1)et if (~x)pour moi, c'est que le premier exprime réellement ce que vous avez l'intention de faire. Ce dernier exprime que vous voulez faire autre chose entièrement ("s'il vous plaît convertir mon nombre 64 bits en un entier 32 bits, et vérifier si au niveau du bit NON de cet entier est vrai"), où vous obtenez simplement le résultat souhaité dans ce un cas.
JimmiTh
10
>= 0était probablement pas leet assez, donc plus cryptique !!~a été utilisé.
Yoshi

Réponses:

56

L'opérateur tilde ne fait pas du tout partie de jQuery - c'est un opérateur NOT au niveau du bit dans JavaScript lui-même.

Voir Le grand mystère du tilde (~) .

Vous obtenez des nombres étranges dans vos expériences parce que vous effectuez une opération logique au niveau du bit sur un entier (qui, pour autant que je sache, peut être stocké en tant que complément à deux ou quelque chose comme ça ...)

Le complément à deux explique comment représenter un nombre en binaire. Je pense que j'avais raison.

pglhall
la source
3
Fixé! (Changé en un autre lien qui, assez bizarrement, a été écrit après ma réponse originale ...)
pglhall
121

Il y a une raison spécifique que vous verrez parfois ~appliquée devant $.inArray.

Fondamentalement,

~$.inArray("foo", bar)

est une façon plus courte de faire

$.inArray("foo", bar) !== -1

$.inArrayrenvoie l'index de l'élément dans le tableau si le premier argument est trouvé, et il renvoie -1 s'il n'est pas trouvé. Cela signifie que si vous recherchez un booléen de "cette valeur est-elle dans le tableau?", Vous ne pouvez pas faire de comparaison booléenne, car -1 est une valeur vraie, et quand $ .inArray renvoie 0 (une valeur falsifiée ), cela signifie qu'il se trouve réellement dans le premier élément du tableau.

L'application de l' ~opérateur -1au niveau du bit fait devenir 0et fait passer 0 à `-1. Ainsi, ne pas trouver la valeur dans le tableau et appliquer le NOT au niveau du bit entraîne une valeur fausse (0), et toutes les autres valeurs renverront des nombres non-0 et représenteront un résultat véridique.

if (~$.inArray("foo", ["foo",2,3])) {
    // Will run
}

Et cela fonctionnera comme prévu.

Yahel
la source
2
Dans quelle mesure cela est-il bien pris en charge dans les navigateurs (maintenant en 2014?) Ou était-il parfaitement pris en charge tout au long?
Explosion Pills
Je serais surpris si des opérations de base comme celles-ci ne seraient pas parfaites.
pcarvalho
104

!!~exprévalue falsequand exprest -1autrement true.
C'est la même chose que expr != -1, seulement cassé *


Cela fonctionne car les opérations JavaScript au niveau du bit convertissent les opérandes en entiers signés 32 bits au format complément à deux. Ainsi !!~-1est évalué comme suit:

   -1 = 1111 1111 1111 1111 1111 1111 1111 1111b // two's complement representation of -1
  ~-1 = 0000 0000 0000 0000 0000 0000 0000 0000b // ~ is bitwise not (invert all bits)
   !0 = true                                     // ! is logical not (true for falsy)
!true = false                                    // duh

Une valeur autre que -1aura au moins un bit mis à zéro; l'inverser créera une valeur de vérité; appliquer l' !opérateur deux fois à une valeur de vérité renvoie la valeur booléenne true.

Lorsqu'il est utilisé avec .indexOf()et nous voulons seulement vérifier si le résultat est -1ou non:

!!~"abc".indexOf("d") // indexOf() returns -1, the expression evaluates to false
!!~"abc".indexOf("a") // indexOf() returns  0, the expression evaluates to true
!!~"abc".indexOf("b") // indexOf() returns  1, the expression evaluates to true

* !!~8589934591évalue à faux donc ceciabominationne peut pas être utilisé de manière fiable pour tester -1.

Salman A
la source
1
Dans une bibliothèque stable, je ne vois aucun problème d'utilisation ~foo.indexOf(bar), ce n'est pas des économies significatives sur les caractères ou les performances, mais c'est un raccourci relativement courant de la même manière foo = foo || {}.
zzzzBov
6
Ce n'est pas un problème ... pas du moins jusqu'à ce que quelqu'un d'autre soit invité à continuer avec votre code.
Salman A
1
@ahsteele, je connais bien cette règle, mais les opérateurs binaires font partie de tous les langages de programmation auxquels je peux penser. J'essaye de programmer d'une manière qui soit lisible par quelqu'un qui peut lire le code . Je n'arrête pas d'utiliser les fonctionnalités d'un langage simplement parce que quelqu'un d'autre ne le comprend pas, sinon je ne pourrais même pas l'utiliser!! .
zzzzBov
Strictement parlant, >= 0n'a pas le même comportement que !!~. !== -1est plus proche.
Peter Olson
33

~foo.indexOf(bar)est un raccourci courant à représenter foo.contains(bar)car la containsfonction n'existe pas.

En général, la conversion en booléen n'est pas nécessaire en raison du concept JavaScript de valeurs "fausses". Dans ce cas, il est utilisé pour forcer la sortie de la fonction à être trueou false.

zzzzBov
la source
6
+1 Cette réponse explique mieux le «pourquoi» que la réponse acceptée.
nalply
18

jQuery.inArray()renvoie -1"non trouvé", dont le complément ( ~) est 0. Ainsi, ~jQuery.inArray()renvoie une valeur falsifiée ( 0) pour "not found", et une valeur de vérité (un entier négatif) pour "found". !!formalisera alors le faux / véridique en vrai booléen false/ true. Donc, !!~jQuery.inArray()donnera truepour "trouvé" et falsepour "non trouvé".

Amadan
la source
13

Le ~pour tous les 4 octets intest égal à cette formule-(N+1)

ALORS

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 
Mina Gabriel
la source
3
Ce n'est pas toujours vrai, puisque (par exemple) ~2147483648 != -(2147483648 + 1).
Frxstrem
10

L' ~opérateur est l'opérateur de complément au niveau du bit. Le résultat entier de inArray()est soit -1, lorsque l'élément n'est pas trouvé, soit un entier non négatif. Le complément bit à bit de -1 (représenté en binaire comme les 1 bits) est zéro. Le complément binaire de tout entier non négatif est toujours différent de zéro.

Ainsi, !!~isera truelorsque l'entier "i" est un entier non négatif, et falselorsque "i" est exactement -1.

Notez que ~contraint toujours son opérande à un entier; c'est-à-dire qu'il force les valeurs à virgule flottante non entières à des nombres entiers, ainsi que des valeurs non numériques.

Pointu
la source
10

Tilde n'est PAS au niveau du bit - il inverse chaque bit de la valeur. En règle générale, si vous utilisez ~sur un nombre, son signe sera inversé, puis 1 sera soustrait.

Ainsi, lorsque vous le faites ~0, vous obtenez -1 (0 inversé est -0, soustrayez 1 est -1).

C'est essentiellement une manière élaborée et ultra-micro-optimisée d'obtenir une valeur toujours booléenne.

Joe
la source
8

Vous avez raison: ce code sera renvoyé falselorsque l' indexOfappel renvoie -1; autrement true.

Comme vous le dites, il serait beaucoup plus judicieux d'utiliser quelque chose comme

return this.modifiedPaths.indexOf(path) !== -1;
LukeH
la source
1
Mais c'est 3 octets de plus à envoyer au client! edit: (juste en plaisantant au fait, j'ai posté mon commentaire et réalisé que ce n'était pas évident (ce qui est à la fois triste et idiot))
Wesley Murch
@Wesley: C'est vrai, mais il ne doit être envoyé qu'une seule fois à chaque client , en supposant que le client mettra en cache le fichier .js. Cela dit, ils pourraient utiliser >=0plutôt que !==-1- pas d'octets supplémentaires à envoyer et toujours plus lisible que la version bit-twiddling.
LukeH
2
Qui traîne qui ici? ;) Je suppose que j'ai pris pour acquis que l'écriture de code lisible est meilleure que le code cryptique pré-optimisé qui génère ce genre de questions. Réduisez simplement le volume plus tard et écrivez du code lisible et compréhensible maintenant.
Wesley Murch
2
Personnellement, je dirais que > -1c'est encore plus lisible, mais c'est probablement très subjectif.
Yoshi
6

L' ~opérateur est l'opérateur NOT au niveau du bit. Cela signifie qu'il prend un nombre sous forme binaire et transforme tous les zéros en uns et les uns en zéros.

Par exemple, le nombre 0 en binaire est 0000000, tandis que -1 l'est 11111111. De même, 1 est 00000001en binaire, tandis que -2 est 11111110.

Frxstrem
la source
3

Je suppose que c'est là parce que c'est quelques caractères plus courts (quels auteurs de bibliothèque recherchent toujours). Il utilise également des opérations qui ne prennent que quelques cycles machine lorsqu'elles sont compilées dans le code natif (par opposition à la comparaison à un nombre).

Je suis d'accord avec une autre réponse selon laquelle c'est exagéré, mais cela pourrait peut-être avoir du sens dans une boucle serrée (nécessite une estimation du gain de performance, cependant, sinon peut s'avérer être une optimisation prématurée.)

Alexandre Pavlov
la source
2

Je suppose que, puisqu'il s'agit d'une opération au niveau du bit, c'est le moyen le plus rapide (peu coûteux en calcul) de vérifier si le chemin apparaît dans modifiedPaths.

panos2point0
la source
1

Comme (~(-1)) === 0, donc:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false
Ingénieur
la source
1
C'est peut-être exact, mais est-ce une explication utile pour le questionneur? Pas du tout. Si je ne l'ai pas compris au départ, une réponse laconique comme celle-ci n'aiderait pas.
Spudley
Je pense que cette réponse a du sens. Si vous avez un cerveau mathématique, vous pouvez voir clairement quelles parties changent à chaque étape. Est-ce la meilleure réponse à cette question? Non, mais c'est utile, je pense que oui! +1
Taylor Lopez