Ceci est valide et renvoie la chaîne de caractères "10"
en JavaScript ( plus d'exemples ici ) :
console.log(++[[]][+[]]+[+[]])
Pourquoi ? Que se passe-t-il ici ?
Ceci est valide et renvoie la chaîne de caractères "10"
en JavaScript ( plus d'exemples ici ) :
console.log(++[[]][+[]]+[+[]])
Pourquoi ? Que se passe-t-il ici ?
Si on le divise, le désordre est égal à :
++[[]][+[]]
+
[+[]]
En JavaScript, il est vrai que +[] === 0
. +
convertit quelque chose en un nombre, et dans ce cas, il s'agit de +""
o 0
(voir les détails des spécifications ci-dessous).
Par conséquent, nous pouvons la simplifier ( ++
a préséance sur +
) :
++[[]][0]
+
[0]
Parce que [[]][0]
signifie : obtenir le premier élément de [[]]
il est vrai que :
[[]][0]
retourne le tableau interne ( []
). En raison des références, il est faux de dire [[]][0] === []
mais appelons le tableau interne A
pour éviter une mauvaise notation.
++
devant son opérande signifie "incrémenter de un et retourner le résultat incrémenté". Ainsi, ++[[]][0]
est équivalent à Number(A) + 1
(ou +A + 1
).
Encore une fois, nous pouvons simplifier ce désordre en quelque chose de plus lisible. Remplaçons []
pour A
:
(+[] + 1)
+
[0]
Avant +[]
peut contraindre le tableau au nombre 0
il doit d'abord être converti en une chaîne de caractères, ce qui est ""
encore. Enfin, 1
est ajouté, ce qui donne 1
.
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Simplifions encore plus :
1
+
[0]
C'est également vrai en JavaScript : [0] == "0"
car il s'agit de joindre un tableau avec un seul élément. La jointure va concaténer les éléments séparés par ,
. Avec un élément, on peut déduire que cette logique aboutira au 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. D'abord, le tableau est converti en chaîne de caractères "0"
Ensuite, le numéro est converti en une chaîne de caractères ( "1"
). Numéro +
Chaîne de caractères ===
Chaîne de caractères .
"1" + "0" === "10" // Yay!
Détails de la spécification pour +[]
:
C'est tout un labyrinthe, mais pour faire +[]
d'abord il est converti en chaîne de caractères parce que c'est ce que +
dice:
11.4.6 Opérateur unaire + (Unary)
L'opérateur unaire + convertit son opérande en type Nombre.
La production UnaryExpression : + UnaryExpression est évaluée comme suit :
Soit expr le résultat de l'évaluation de UnaryExpression.
Retourner ToNumber(GetValue(expr)).
ToNumber()
dice:
Objet
Appliquez les étapes suivantes :
Soit primValue ToPrimitive(input argument, hint String).
Retournez ToString(primValue).
ToPrimitive()
dice:
Objet
Renvoie une valeur par défaut pour l'objet. La valeur par défaut d'un objet est récupérée en appelant la méthode interne [[DefaultValue]] de l'objet, en passant l'indice optionnel PreferredType. Le comportement de la méthode interne [[DefaultValue]] est défini par la présente spécification pour tous les objets ECMAScript natifs dans le paragraphe 8.12.8.
[[DefaultValue]]
dice:
8.12.8 [[Valeur par défaut]] (hint)
Lorsque la méthode interne [[DefaultValue]] de O est appelée avec l'indice String, les étapes suivantes sont réalisées :
Soit toString le résultat de l'appel de la méthode interne [[Get]] de l'objet O avec l'argument "toString".
Si IsCallable(toString) est vrai alors,
a. Soit str le résultat de l'appel de la méthode interne [[Call]] de toString, avec O comme valeur et une liste d'arguments vide.
b. Si str est une valeur primitive, retourne str.
En .toString
d'un tableau dit :
15.4.4.2 Array.prototype.toString ( )
Lorsque la méthode toString est appelée, les étapes suivantes sont réalisées :
Soit le tableau le résultat de l'appel à ToObject sur cette valeur.
Soit func le résultat de l'appel de la méthode interne [[Get]] du tableau avec l'argument "join".
Si IsCallable(func) est faux, alors func est la méthode intégrée standard Object.prototype.toString (15.2.4.2).
Retourne le résultat de l'appel de la méthode interne [[Call]] de func en fournissant array comme valeur et une liste d'arguments vide.
Alors +[]
se résume à +""
parce que [].join() === ""
.
Encore une fois, le +
est défini comme suit :
11.4.6 Opérateur unaire + (Unary)
L'opérateur unaire + convertit son opérande en type Nombre.
La production UnaryExpression : + UnaryExpression est évaluée comme suit :
Soit expr le résultat de l'évaluation de UnaryExpression.
Retourner ToNumber(GetValue(expr)).
ToNumber
est défini pour ""
comme :
Le MV de StringNumericLiteral :: : [vide] est 0.
Alors +"" === 0
et donc +[] === 0
.
@Lie Ryan : [[]][0]
est que l'emboîtement []
. ++
s'incrémente de un, et renvoie donc [] + 1
.
Suggestion : faire en sorte que le " ++[[]][0]
a [] + 1
" transformation explicite dans le post. Cela rend l'ensemble beaucoup plus compréhensible pour ceux d'entre nous qui ne connaissent pas vraiment JavaScript !
Le texte suivant est adapté d'un article de blog répondant à cette question que j'ai postée alors que cette question était encore fermée. Les liens renvoient à (une copie HTML de) la spécification ECMAScript 3, qui reste la référence pour JavaScript dans les navigateurs Web les plus utilisés aujourd'hui.
Tout d'abord, un commentaire : ce type d'expression n'apparaîtra jamais dans un environnement de production (sain) et n'est utile qu'en tant qu'exercice pour montrer à quel point le lecteur connaît les limites du JavaScript. Le principe général selon lequel les opérateurs JavaScript convertissent implicitement les types est utile, tout comme certaines des conversions courantes, mais la plupart des détails dans ce cas ne le sont pas.
L'expression ++[[]][+[]]+[+[]]
peut, au premier abord, sembler plutôt imposante et obscure, mais elle 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'elles ne changent rien, mais si vous voulez le vérifier, n'hésitez pas à vous documenter sur l'expression opérateur de groupage . Ainsi, l'expression peut être plus clairement écrite comme suit
( ++[[]][+[]] ) + ( [+[]] )
En décomposant cela, nous pouvons simplifier en observant que +[]
évalue à 0
. Pour vous convaincre de la véracité de cette affirmation, consultez la page opérateur unaire + et suivre le sentier légèrement tortueux qui aboutit à ToPrimitive convertissant le tableau vide en une chaîne vide, qui est ensuite finalement convertie en 0
par ToNumber . Nous pouvons maintenant substituer 0
pour chaque instance de +[]
:
( ++[[]][0] ) + [0]
C'est déjà plus simple. Quant à ++[[]][0]
c'est une combinaison de opérateur d'incrémentation du préfixe ( ++
), un tableau littéral définir un tableau avec un seul élément 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 du tableau.
Donc, nous pouvons simplifier [[]][0]
à juste []
et nous avons ++[]
n'est-ce pas ? En fait, ce n'est pas le cas car en évaluant ++[]
lance une erreur, ce qui peut sembler déroutant au départ. Cependant, une petite réflexion sur la nature de ++
le précise : il est utilisé pour incrémenter une variable (ex. ++i
) ou une propriété d'objet (par exemple ++obj.count
). Non seulement elle est évaluée à une valeur, mais elle stocke également cette valeur quelque part. Dans le cas de ++[]
il n'a nulle part où placer la nouvelle valeur (quelle qu'elle soit) car il n'y a pas de référence à une propriété ou une variable d'objet à mettre à jour. En termes de spécification, cela est couvert par la fonction interne PutValue qui est appelée par l'opérateur d'incrémentation du préfixe.
Alors, que fait ++[[]][0]
faire ? Eh bien, par une logique similaire à celle +[]
le tableau intérieur est converti en 0
et cette valeur est incrémentée par 1
pour nous donner une valeur finale de 1
. La valeur de la propriété 0
dans le tableau extérieur est mis à jour en 1
et l'expression entière est évaluée à 1
.
Cela nous laisse avec
1 + [0]
... qui est une simple utilisation de la fonction opérateur d'addition . Les deux opérandes sont d'abord convertis en primitives et si l'une ou l'autre des valeurs primitives est une chaîne de caractères, une concaténation de chaînes de caractères est effectuée, sinon une addition numérique est effectuée. [0]
se convertit en "0"
La concaténation des chaînes de caractères est donc utilisée, ce qui donne "10"
.
En guise de conclusion, une chose qui n'est peut-être pas immédiatement apparente, c'est que le fait de remplacer l'une ou l'autre des fonctions toString()
o valueOf()
méthodes de Array.prototype
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 une valeur primitive. Par exemple, l'expression suivante
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... produit "NaNfoo"
. La raison pour laquelle cela se produit est laissée comme un exercice pour le lecteur...
Celui-ci a la même évaluation mais est un peu plus petit.
+!![]+''+(+[])
donc il est évalué à
+(true) + '' + (0)
1 + '' + 0
"10"
Maintenant que vous avez compris ça, essayez celui-là :
_=$=+[],++_+''+$
Eh bien non, il évalue toujours à "10". Cependant, cela se fait d'une manière différente. Essayez de l'évaluer dans un inspecteur javascript comme chrome ou autre.
_=$=+[],++_+''+$ -> _=$=0,++_+''+$ -> _=0,$=0,++_+''+$ -> ++0+''+0 -> 1+''+0 -> '10' // Yei :v
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
541 votes
Commencez par comprendre que
+[]
convertit un tableau vide en0
... puis perdre un après-midi... ;)17 votes
Related stackoverflow.com/questions/4170978/explain-why-this-works .
15 votes
Jetez un coup d'œil à wtfjs.com - il y a pas mal de choses comme ça avec des explications.
4 votes
@deceze, où apprends-tu ce genre de choses ? Quels livres ? J'apprends JS sur MDN et ils n'enseignent pas ces choses.
8 votes
@SiddharthThevaril De la même manière que vous venez de le faire : quelqu'un a posté un message à ce sujet quelque part et je l'ai lu par hasard.
5 votes
@SiddharthThevaril Ce ne sont pas vraiment des choses que vous devriez utiliser dans la programmation normale. C'est juste une bizarrerie du duck-typing et du casting implicite. Vous avez probablement vu que vous pouvez utiliser
!
pour transformer un nombre en booléen. Vous pouvez utiliser+
pour transformer un booléen ou une chaîne de caractères en un nombre. À partir de là, tout ce dont vous avez besoin est de déterminer quelles sont les valeurs qui renvoient du vrai (convertir en vrai ou en 1) et quelles sont les valeurs qui renvoient du faux (convertir en faux ou en 0).2 votes
Une autre question tout aussi importante est "pourquoi feriez-vous cela ?".