Ceci est valide et renvoie la chaîne "10"
en JavaScript ( plus d'exemples ici ):
console.log(++[[]][+[]]+[+[]])
Pourquoi? Que se passe-t-il ici?
javascript
syntax
SapuSeven
la source
la source
+[]
jette un tableau vide dans0
... puis perdez un après-midi ...;)Réponses:
Si nous le séparons, le désordre est égal à:
En JavaScript, c'est vrai que
+[] === 0
.+
convertit quelque chose en nombre, et dans ce cas, il descendra à+""
ou0
(voir les détails de la spécification ci-dessous).Par conséquent, nous pouvons le simplifier (
++
a priorité sur+
):Parce que
[[]][0]
signifie: obtenir le premier élément de[[]]
, il est vrai que:[[]][0]
renvoie le tableau intérieur ([]
). En raison de références, il est faux de le dire[[]][0] === []
, mais appelons le tableau interneA
pour éviter la mauvaise notation.++
avant son opérande signifie «incrémenter de un et retourner le résultat incrémenté».++[[]][0]
Est donc équivalent àNumber(A) + 1
(ou+A + 1
).Encore une fois, nous pouvons simplifier le désordre en quelque chose de plus lisible. Remplaçons de
[]
retourA
:Avant de
+[]
pouvoir contraindre le tableau dans le nombre0
, il doit d'abord être contraint dans une chaîne, ce qui est""
encore une fois. Enfin,1
est ajouté, ce qui se traduit par1
.(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Simplifions encore plus:
En outre, cela est vrai en JavaScript:,
[0] == "0"
car il joint un tableau avec un élément. La jointure concaténera les éléments séparés par,
. Avec un élément, vous pouvez déduire que cette logique se traduira par le premier élément lui-même.Dans ce cas,
+
voit deux opérandes: un nombre et un tableau. Il essaie maintenant de contraindre les deux dans le même type. Tout d'abord, le tableau est contraint dans la chaîne"0"
, ensuite, le nombre est contraint dans une chaîne ("1"
). Chaîne de nombre Chaîne+
===
.Détails des spécifications pour
+[]
:C'est tout un labyrinthe, mais pour ce faire
+[]
, il est d'abord converti en chaîne car c'est ce qui+
dit:ToNumber()
dit:ToPrimitive()
dit:[[DefaultValue]]
dit:Le
.toString
tableau indique:Cela
+[]
revient donc à+""
, parce que[].join() === ""
.Encore une fois, le
+
est défini comme:ToNumber
est défini""
comme:Donc
+"" === 0
, et donc+[] === 0
.la source
true
si la valeur et le type sont identiques.0 == ""
renvoietrue
(même après la conversion de type), mais0 === ""
estfalse
(pas les mêmes types).1 + [0]
, non"1" + [0]
, car l'++
opérateur prefix ( ) renvoie toujours un nombre. Voir bclary.com/2004/11/07/#a-11.4.4++[[]][0]
renvoie en effet1
, mais++[]
renvoie une erreur. C'est remarquable car il semble que++[[]][0]
cela se résume à++[]
. Avez-vous peut-être une idée de pourquoi++[]
jette une erreur alors++[[]][0]
que non?PutValue
appel (dans la terminologie ES3, 8.7.2) dans l'opération de préfixe.PutValue
nécessite une référence alors[]
qu'une expression à elle seule ne produit pas de référence. Une expression contenant une référence variable (disons que nous avions précédemment définievar a = []
puis++a
fonctionne) ou l'accès à la propriété d'un objet (tel que[[]][0]
) produit une référence. En termes plus simples, l'opérateur de préfixe produit non seulement une valeur, il a également besoin d'un endroit pour mettre cette valeur.var a = []; ++a
,a
est égal à 1. Après l'exécution++[[]][0]
, le tableau créé par l'[[]]
expression contient désormais uniquement le numéro 1 à l'index 0.++
nécessite une référence pour ce faire.Ensuite, nous avons une concaténation de chaînes
la source
===
plutôt que=>
?Ce qui suit est adapté d'un article de blog répondant à cette question que j'ai posté alors que cette question était encore fermée. Les liens sont vers (une copie HTML de) la spécification ECMAScript 3, toujours la référence pour JavaScript dans les navigateurs Web couramment utilisés aujourd'hui.
Tout d'abord, un commentaire: ce type d'expression ne se présentera jamais dans un environnement de production (sain) et n'est utile que comme exercice pour savoir à quel point le lecteur connaît les bords sales de JavaScript. Le principe général selon lequel les opérateurs JavaScript convertissent implicitement entre les types est utile, comme le sont certaines des conversions courantes, mais la plupart des détails dans ce cas ne le sont pas.
L'expression
++[[]][+[]]+[+[]]
peut initialement sembler plutôt imposante et obscure, mais est en fait relativement facile à décomposer en expressions distinctes. Ci-dessous, j'ai simplement ajouté des parenthèses pour plus de clarté; Je peux vous assurer qu'ils ne changent rien, mais si vous voulez le vérifier, n'hésitez pas à vous renseigner sur l' opérateur de regroupement . Ainsi, l'expression peut être écrite plus clairement commeEn décomposant cela, nous pouvons simplifier en observant qui
+[]
évalue à0
. Pour vous convaincre de la raison pour laquelle cela est vrai, consultez l' opérateur unaire + et suivez la piste légèrement tortueuse qui se termine par ToPrimitive convertissant le tableau vide en une chaîne vide, qui est ensuite finalement convertie en0
par ToNumber . Nous pouvons maintenant remplacer0
chaque instance de+[]
:Déjà plus simple. Quant à
++[[]][0]
, c'est une combinaison de l' opérateur d'incrémentation de préfixe (++
), un littéral de tableau définissant un tableau avec un élément unique qui est lui-même un tableau vide ([[]]
) et un accesseur de propriété ([0]
) appelé sur le tableau défini par le littéral de tableau.Donc, nous pouvons simplifier
[[]][0]
juste[]
et nous avons++[]
, non? En fait, ce n'est pas le cas car l'évaluation++[]
génère une erreur, ce qui peut sembler initialement déroutant. Cependant, un peu de réflexion sur la nature de++
cela le montre clairement: il est utilisé pour incrémenter une variable (par exemple++i
) ou une propriété d'objet (par exemple++obj.count
). Non seulement il évalue une valeur, mais il stocke également cette valeur quelque part. Dans le cas de++[]
, il n'a nulle part où mettre la nouvelle valeur (quelle qu'elle soit) car il n'y a aucune référence à une propriété ou variable d'objet à mettre à jour. En termes de spécification, cela est couvert par l' opération PutValue interne , qui est appelée par l'opérateur d'incrémentation de préfixe.Alors, qu'est-ce que ça
++[[]][0]
fait? Eh bien, selon une logique similaire à+[]
, le tableau interne est converti en0
et cette valeur est incrémentée de1
pour nous donner une valeur finale de1
. La valeur de la propriété0
dans le tableau externe est mise à jour1
et l'expression entière est évaluée1
.Cela nous laisse avec
... qui est une utilisation simple de l' opérateur d'addition . Les deux opérandes sont d'abord convertis en primitives et si l'une ou l'autre valeur primitive est une chaîne, la concaténation de chaîne est effectuée, sinon l'addition numérique est effectuée.
[0]
convertit en"0"
, donc la concaténation de chaînes est utilisée, produisant"10"
.Enfin, quelque chose qui peut ne pas être immédiatement apparent est que le remplacement de l'une des méthodes
toString()
ou changera le résultat de l'expression, car les deux sont vérifiés et utilisés s'ils sont présents lors de la conversion d'un objet en valeur primitive. Par exemple, les éléments suivantsvalueOf()
Array.prototype
... produit
"NaNfoo"
. Pourquoi cela se produit est laissé comme un exercice pour le lecteur ...la source
Rendons les choses simples:
la source
Celui-ci est identique mais un peu plus petit
il en va de même pour
Alors maintenant vous avez cela, essayez celui-ci:
la source
"10"
+ [] évalue à 0 [...] puis l'addition (+ opération) avec n'importe quoi convertit le contenu du tableau en sa représentation sous forme de chaîne composée d'éléments joints par une virgule.
Tout autre chose comme prendre l'index du tableau (avoir une priorité de râpe supérieure à + opération) est ordinale et n'a rien d'intéressant.
la source
Les moyens les plus courts possibles d'évaluer une expression en "10" sans chiffres sont peut-être les suivants:
+!+[] + [+[]]
// "dix"-~[] + [+[]]
// "dix"// ========== Explication ========== \\
+!+[]
:+[]
Convertit en 0.!0
convertit entrue
.+true
convertit en 1.-~[]
=-(-1)
qui est 1[+[]]
:+[]
Convertit en 0.[0]
est un tableau avec un seul élément 0.Ensuite, JS évalue l' expression
1 + [0]
, ainsiNumber + Array
. Ensuite, la spécification ECMA fonctionne: l'+
opérateur convertit les deux opérandes en une chaîne en appelant lestoString()/valueOf()
fonctions à partir duObject
prototype de base . Il fonctionne comme une fonction additive si les deux opérandes d'une expression sont uniquement des nombres. L'astuce est que les tableaux convertissent facilement leurs éléments en une représentation sous forme de chaîne concaténée.Quelques exemples:
Il y a une belle exception: deux
Objects
ajouts entraînentNaN
:la source
+ '' ou + [] évalue 0.
la source
[]
n'est pas équivalent à""
. L'élément est d'abord extrait, puis converti par++
.Pas à pas de cela,
+
transformez la valeur en nombre et si vous ajoutez à un tableau vide+[]
... comme il est vide et est égal à0
, ilAlors à partir de là, regardez maintenant votre code, c'est
++[[]][+[]]+[+[]]
...Et il y a plus entre eux
++[[]][+[]]
+[+[]]
Donc, ceux-
[+[]]
ci reviendront[0]
car ils ont un tableau vide qui est converti à l'0
intérieur de l'autre tableau ...Donc, comme imaginez, la première valeur est un tableau à 2 dimensions avec un tableau à l'intérieur ...
[[]][+[]]
sera donc égal à celui[[]][0]
qui retournera[]
...Et à la fin,
++
convertissez-le et augmentez-le pour1
...Vous pouvez donc imaginer,
1
+"0"
sera"10"
...la source