Que fait un tilde quand il précède une expression?

Réponses:

267

~est un opérateur au niveau du bit qui retourne tous les bits de son opérande.

Par exemple, si votre numéro était 1, sa représentation binaire du flottant IEEE 754 (comment JavaScript traite les nombres) serait ...

0011 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

~Convertit donc son opérande en un entier 32 bits (les opérateurs au niveau du bit en JavaScript le font) ...

0000 0000 0000 0000 0000 0000 0000 0001

S'il s'agissait d'un nombre négatif, il serait stocké dans le complément à 2: inversez tous les bits et ajoutez 1.

... puis retourne tous ses bits ...

1111 1111 1111 1111 1111 1111 1111 1110

Alors à quoi ça sert alors? Quand pourrait-on jamais l'utiliser?

Il a plusieurs utilisations. Si vous écrivez des trucs de bas niveau, c'est pratique. Si vous avez profilé votre application et trouvé un goulot d'étranglement, il pourrait être rendu plus performant en utilisant des astuces au niveau du bit (comme un outil possible dans un sac beaucoup plus grand).

Il est aussi un (généralement) pas clair truc à tourner indexOf()est trouvé valeur de retour dans truthy (tout en faisant pas trouvé que falsy ) et les gens utilisent souvent pour son effet secondaire de tronquer nombres à 32 bits (et laissant tomber sa place décimale en le doublant, effectivement les mêmes que Math.floor()pour les nombres positifs).

Je dis pas clair, car il n'est pas immédiatement évident à quoi il sert. Généralement, vous voulez que votre code communique clairement aux autres personnes qui le lisent. Bien que l'utilisation ~puisse sembler cool , elle est généralement trop intelligente pour son propre bien. :)

C'est aussi moins pertinent maintenant que JavaScript a Array.prototype.includes()et String.prototype.includes(). Ceux-ci renvoient une valeur booléenne. Si votre ou vos plates-formes cibles le prennent en charge, vous devriez le préférer pour tester l'existence d'une valeur dans une chaîne ou un tableau.

alex
la source
2
Nasty est-il le bon mot? Si cela fonctionne, je l'appellerais simplement un idiome de la langue. Il y a beaucoup d'expressions idiomatiques. Une fois que vous les apprenez, ils ne sont pas flous. Les compréhensions de liste ne sont pas claires en Python si vous ne les connaissez pas et peuvent être accomplies avec des boucles plus verbeuses mais vous ne demanderez jamais à un programmeur Python de ne pas les utiliser. De même value = value || defaulten JavaScript est un idiome courant et valide tant que vous savez quand vous pouvez et ne pouvez pas l'utiliser.
gman
3
@gman Je suppose que peu importe que quelqu'un l'utilise ou non. Je pense que comparer des compréhensions de liste (fonctionnalité de langue) à ce n'est pas vraiment la même chose ( un moyen intelligent pour éviter de taper des caractères supplémentaires). Si vous pensez que le terme méchant est trop sévère, n'hésitez pas à modifier ma réponse.
alex
2
Peut-être un exemple plus courant est v = t ? a : b;. Je trouve cela beaucoup plus clair que d' var v; if (t} { v = a; } else { v = b; }habitude réparti sur 5+ lignes et aussi plus clair que var v = b; if (t) { v = a; }ce qui serait généralement 4+ lignes. Mais je connais beaucoup de gens qui ne connaissent pas les ? :opérateurs qui préféreraient la deuxième ou la troisième voie. Je trouve que le premier est plus lisible. Je suis d'accord avec le principe général, clarifiez le code, n'utilisez pas de hacks. Je suppose que je vois juste ~v.indexOf('...')être très clair une fois que je l'ai appris.
gman
9
Lorsque vous travaillez dans une grande entreprise avec de nombreux développeurs, vous voulez que le code soit clairement écrit (ANGLAIS) et bien documenté. Le but du codage de haut niveau dans un langage avec garbage collection est d'éviter de penser aux opérations binaires, et de nombreux développeurs frontaux n'ont même pas d'expérience en langage d'assemblage sous leur ceinture.
user2867288
3
je n'appellerais pas ~idiomatique. cela fait techniquement partie de la spécification de langue , mais ce n'est pas tellement une partie de la langue en général .
worc
109

L'utiliser avant une indexOf()expression vous donne effectivement un résultat vrai / faux au lieu de l'index numérique qui est directement renvoyé.

Si la valeur de retour est -1, alors ~-1c'est 0parce que -1c'est une chaîne de tous les 1 bits. Toute valeur supérieure ou égale à zéro donnera un résultat différent de zéro. Donc,

if (~someString.indexOf(something)) {
}

entraînera l' ifexécution du code lorsque "quelque chose" est dans "someString". Si vous essayez de l'utiliser .indexOf()directement en tant que booléen, cela ne fonctionnera pas, car il retourne parfois zéro (lorsque "quelque chose" est au début de la chaîne).

Bien sûr, cela fonctionne aussi:

if (someString.indexOf(something) >= 0) {
}

et c'est beaucoup moins mystérieux.

Parfois, vous verrez également ceci:

var i = ~~something;

L'utilisation de l' ~opérateur deux fois comme cela est un moyen rapide de convertir une chaîne en un entier 32 bits. Le premier ~effectue la conversion et le second ~retourne les bits. Bien sûr, si l'opérateur est appliqué à quelque chose qui ne peut pas être converti en nombre, vous obtenez NaNle résultat. ( modifier - en fait, c'est la seconde ~qui est appliquée en premier, mais vous avez l'idée.)

Pointu
la source
2
Pour ceux qui ne veulent pas nier bit par bit, ~lorsqu'il est effectué sur des entiers est égal à -(x + 1).
Fabrício Matté
Il semble que les valeurs numériques NÉGATIVES devraient également renvoyer des valeurs booléennes négatives en premier lieu. Mais juste un autre échec de JS, je suppose?
wwaawaw
7
@adlwalrus remonte bien à la tradition de l' 0être falseet de l' être non nul true, du moins au C dans les années 70 et probablement à beaucoup d'autres langages de programmation de systèmes alors contemporains. Cela découle probablement du fonctionnement du matériel; de nombreux processeurs définissent un bit zéro après une opération et ont une instruction de branchement correspondante pour la tester.
Pointy
4
Un moyen plus rapide de le convertir en 32 bits int serait | 0, auquel cas sa seule opération.
alex
@alex en effet, bien que nous ne puissions pas nécessairement faire confiance au runtime pour ne pas interpréter une simple application ~~exactement de la même manière.
Pointy
29

L' opérateur~ est Bitwise NOT , ~xest à peu près le même que -(x+1). C'est plus facile à comprendre, en quelque sorte. Alors:

~2;    // -(2+1) ==> -3

Considérez -(x+1). -1peut effectuer cette opération pour produire un 0.

En d'autres termes, ~utilisé avec une plage de valeurs numériques produira une valeur fausse (contraindre à falsepartir de 0) uniquement pour la -1valeur d'entrée, sinon, toute autre valeur vraie.

Comme nous le savons, -1est communément appelé une valeur sentinelle . Il est utilisé pour de nombreuses fonctions qui renvoient des >= 0valeurs de réussite et -1d' échec en langage C. Dont la même règle de valeur de retour indexOf()en JavaScript.

Il est courant de vérifier la présence / absence d'une sous-chaîne dans une autre chaîne de cette manière

var a = "Hello Baby";

if (a.indexOf("Ba") >= 0) {
    // found it
}
if (a.indexOf("Ba") != -1) { 
    // found it
}

if (a.indexOf("aB") < 0) { 
    // not found
}
if (a.indexOf( "aB" ) == -1) { 
    // not found
}

Cependant, il serait plus facile de le faire ~comme ci-dessous

var a = "Hello Baby";

~a.indexOf("Ba");         // -7   -> truthy
if (~a.indexOf("Ba")) {   // true
    // found it
}

~a.indexOf("aB");         // 0    -> falsy
!~a.indexOf("aB");        // true
if (!~a.indexOf( "aB" )) {  // true
    // not found
}

You Don't Know JS: Types & Grammar par Kyle Simpson

zangw
la source
1
C'est certainement plus facile à comprendre à sa valeur nominale, même si une personne ne comprend pas le contexte qui le fait fonctionner. Je regarderais de nouveau -(x+1)si je l'ai vu dans une déclaration if. Le tilde me dit exactement ce qu'il fait pour compenser la nature basée sur 0 de Javascript. Aussi, moins il y a de parenthèses, mieux c'est pour lire
Regular Joe
Dans votre bloc de code de contrôle initial, vous pouvez taper moins en utilisant if (a.indexOf("Ba") > -1) {// found} //truece qui, bien qu'un peu plus long que les exemples tilde, est considérablement moins que les deux exemples que vous avez donnés et pour les nouveaux programmeurs var opinion = !~-1 ? 'more' : 'less'compréhensibles.
HalfMens
24

~indexOf(item) revient assez souvent, et les réponses ici sont excellentes, mais peut-être que certaines personnes ont juste besoin de savoir comment l'utiliser et "sauter" la théorie:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }
Jorge Bucaran
la source
1
Je suis d'accord. Le guide de style JavaScript d'Airbnb interdit ++et --parce qu'ils "encouragent les ruses excessives" et pourtant ont ~survécu (caché dans l'ombre) github.com/airbnb/javascript/issues/540
Shanimal
@Shanimal L'alternative est list.indexOf(item) >= 0or ... > -1puisque javascript est basé sur zéro et n'a pas choisi de résoudre ce problème dès le départ. De plus, juste une opinion (identique à celle d'Airbnb), quiconque fait quelque chose de significatif en javascript le sait ++, et bien que ce --soit moins courant, le sens peut être inféré.
Regular Joe
@RegularJoe Je suis d'accord avec la plupart de cela. Personnellement , j'ai pas besoin ++et --dans un certain temps en raison des méthodes primitives comme map, forEachetc. Mon point est plus pourquoi ils ne considéraient pas aussi ~excessivement difficile quand tout standard utilisé comprend incrémentation et les opérateurs décrémentation. Interdire quelque chose pour que CIS101 n'a aucun sens.
Shanimal
11

Pour ceux qui envisagent d'utiliser l'astuce tilde pour créer une valeur véridique à partir d'un indexOfrésultat, il est plus explicite et a moins de magie pour utiliser la includesméthode à la placeString .

'hello world'.includes('hello') //=> true
'hello world'.includes('kittens') //=> false

Notez qu'il s'agit d'une nouvelle méthode standard à partir d'ES 2015, elle ne fonctionnera donc pas sur les anciens navigateurs. Dans les cas où cela est important, envisagez d'utiliser le polyfill String.prototype.includes .

Cette fonctionnalité est également disponible pour les tableaux utilisant la même syntaxe :

['apples', 'oranges', 'cherries'].includes('apples') //=> true
['apples', 'oranges', 'cherries'].includes('unicorns') //=> false

Voici le polyfill Array.prototype.includes si vous avez besoin d'un support de navigateur plus ancien.

Dana Woodman
la source
2
Évitez d'utiliser includes (). Il n'est pris en charge dans aucune version d'IE (pas seulement les navigateurs plus anciens) au moment de la rédaction: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Onshop
8
Ou utilisez simplement un compilateur, afin que vous puissiez écrire un code plus clair, au lieu d'écrire la langue selon le pire interprète JS du dénominateur commun là-bas ...
Kyle Baker
2
@Ben a raison, cela ne fonctionne pas non plus dans Netscape 4.72.
mikemaccana