web-dev-qa-db-fra.com

Pourquoi ++ [[]] [+ []] + [+ []] renvoie la chaîne "10"?

Ceci est valide et renvoie la chaîne "10" en JavaScript ( plus d'exemples ici ):

console.log(++[[]][+[]]+[+[]])

Pourquoi? Que se passe-t-il ici?

1561
JohnJohnGa

Si on le scinde, le désordre est égal à:

++[[]][+[]]
+
[+[]]

En JavaScript, il est vrai que +[] === 0. + convertit quelque chose en un nombre et dans ce cas, il sera réduit à +"" ou 0 (voir les détails de la spécification ci-dessous).

Par conséquent, nous pouvons le simplifier (++ a la préséance sur +):

++[[]][0]
+
[0]

Parce que [[]][0] signifie: obtenir le premier élément de [[]], il est vrai que:

[[]][0] renvoie le tableau interne ([]). En raison des références, il est faux de dire [[]][0] === [], mais appelons le tableau interne A pour éviter la mauvaise notation.

++ avant son opérande signifie "incrémenter de un et renvoyer le résultat incrémenté". Donc, ++[[]][0] équivaut à Number(A) + 1 (ou +A + 1).

Encore une fois, nous pouvons simplifier le désordre en quelque chose de plus lisible. Remplaçons [] par A:

(+[] + 1)
+
[0]

Avant que +[] puisse forcer le tableau dans le nombre 0, il doit d'abord être contraint dans une chaîne, qui est "". Enfin, 1 est ajouté, ce qui donne 1.

  • (+[] + 1) === (+"" + 1)
  • (+"" + 1) === (0 + 1)
  • (0 + 1) === 1

Simplifions encore plus:

1
+
[0]

Cela est également vrai en JavaScript: [0] == "0", car il joint un tableau à un élément. La jonction va concaténer les éléments séparés par ,. Avec un élément, vous pouvez en déduire que cette logique donnera 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", puis le nombre est contraint dans une chaîne ("1"). Numéro + Chaîne === Chaîne.

"1" + "0" === "10" // Yay!

Détails de spécification pour +[]:

C'est tout un labyrinthe, mais pour faire +[], il est d'abord converti en chaîne car c'est ce que + dit:

11.4.6 opérateur unaire +

L'opérateur unaire + convertit son opérande en type numérique.

La production UnaryExpression: + UnaryExpression est évaluée comme suit:

  1. Soit expr le résultat de l’évaluation de UnaryExpression.

  2. Retourne au numéro (GetValue (expr)).

ToNumber() dit:

Objet

Appliquez les étapes suivantes:

  1. Soit primValue ToPrimitive (argument d'entrée, hint String).

  2. Return ToString (primValue).

ToPrimitive() dit:

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 transmettant l'indicateur facultatif PreferredType. Le comportement de la méthode interne [[DefaultValue]] est défini par cette spécification pour tous les objets ECMAScript natifs dans 8.12.8.

[[DefaultValue]] dit:

8.12.8 [[DefaultValue]] (indice)

Lorsque la méthode interne [[DefaultValue]] de O est appelée avec hint String, les étapes suivantes sont entreprises:

  1. Soit toString le résultat de l'appel de la méthode interne [[Get]] de l'objet O avec l'argument "toString".

  2. Si IsCallable (toString) est true, alors,

une. Soit str le résultat de l'appel de la méthode interne [[Call]] de toString, avec O comme valeur this et une liste d'arguments vide.

b. Si str est une valeur primitive, retourne str.

Le .toString d'un tableau dit:

15.4.4.2 Array.prototype.toString ()

Lorsque la méthode toString est appelée, les étapes suivantes sont effectuées:

  1. Soit array le résultat de l'appel de ToObject sur la valeur this.

  2. Laissez func le résultat de l'appel de la méthode interne [[Get]] du tableau avec l'argument "join".

  3. Si IsCallable (func) est false, laissez func comme méthode intégrée standard Object.prototype.toString (15.2.4.2).

  4. Renvoie le résultat de l'appel de la méthode interne [[Call]] de func en fournissant un tableau sous la forme this et une liste d'arguments vide.

Donc, +[] revient à +"", car [].join() === "".

Encore une fois, le + est défini comme suit:

11.4.6 opérateur unaire +

L'opérateur unaire + convertit son opérande en type numérique.

La production UnaryExpression: + UnaryExpression est évaluée comme suit:

  1. Soit expr le résultat de l’évaluation de UnaryExpression.

  2. Retourne au numéro (GetValue (expr)).

ToNumber est défini pour "" comme:

Le MV de StringNumericLiteral ::: [empty] est 0.

Donc, +"" === 0, et donc +[] === 0.

2009
pimvdb
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Ensuite, nous avons une concaténation de chaîne

1+[0].toString() = 10
113
Shef

Ce qui suit est adapté d’un article de blog répondant à cette question que j’avais postée alors que cette question était encore fermée. Les liens renvoient vers (une copie HTML de) la spécification ECMAScript 3, qui reste la base de référence pour JavaScript dans les navigateurs Web couramment utilisés.

Tout d’abord, un commentaire: ce type d’expression ne se retrouvera jamais dans aucun environnement de production (sain), il n’est utile que dans la mesure où le lecteur connaît bien les limites de JavaScript. Le principe général selon lequel les opérateurs JavaScript convertissent implicitement entre les types est utile, de même que certaines des conversions courantes, mais la plupart des détails dans ce cas ne le sont pas.

L’expression ++[[]][+[]]+[+[]] peut sembler plutôt imposante et obscure au début, mais elle est en fait relativement facile à décomposer en expressions séparées. 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 vérifier cela, n'hésitez pas à en savoir plus sur l'opérateur opérateur de groupement . Ainsi, l'expression peut être plus clairement écrite comme

( ++[[]][+[]] ) + ( [+[]] )

En décomposant cela, nous pouvons simplifier en observant que +[] est évalué à 0. Pour vous assurer que cela est vrai, vérifiez le opérateur unaire + et suivez la piste légèrement tortueuse qui aboutit à ToPrimitive convertissant le tableau vide en une chaîne vide, qui est ensuite converti en 0 par ToNumber . Nous pouvons maintenant substituer 0 à chaque instance de +[]:

( ++[[]][0] ) + [0]

Plus simple déjà. En ce qui concerne ++[[]][0], il s'agit d'une combinaison de opérateur d'incrément de préfixe (++), d'un littéral de tableau définissant 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 de tableau.

Donc, nous pouvons simplifier [[]][0] en [] 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 ++ le précise: 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, 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 pas de référence à une propriété d'objet ou à une variable à mettre à jour. En termes spécifiques, cela est couvert par l'opération interne PutValue , appelée par l'opérateur d'incrément de préfixe.

Alors, que fait ++[[]][0]? Eh bien, par une logique similaire à +[], le tableau interne est converti en 0 et cette valeur est incrémentée de 1 pour nous donner une valeur finale de 1. La valeur de la propriété 0 dans le tableau externe est mise à jour à 1 et l'expression entière est évaluée à 1.

Cela nous laisse avec

1 + [0]

... qui est une utilisation simple de opérateur d'addition . Les deux opérandes sont d'abord convertis en primitives et si l'une des primitives est une chaîne, une concaténation de chaîne est effectuée, sinon une addition numérique est effectuée. [0] est converti en "0"; une concaténation de chaîne est donc utilisée pour produire "10".

Enfin, il est possible que le remplacement de l'une des méthodes toString() ou valueOf() de Array.prototype modifie le résultat de l'expression, car les deux sont vérifiées et utilisées si elles sont présentes lors de la conversion d'un objet en valeur primitive. Par exemple, le suivant

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

... produit "NaNfoo". Pourquoi cela se produit est laissé comme un exercice pour le lecteur ...

59
Tim Down

Soyons simples:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"
22
renatoluna

Celui-ci évalue le même mais un peu plus petit

+!![]+''+(+[])
  • [] - est un tableau est converti qui est converti en 0 lorsque vous ajoutez ou soustrayez de celui-ci, donc par conséquent + [] = 0
  • ! [] - évalue à false, donc !! [] évalue à true
  • + !! [] - convertit le vrai en une valeur numérique évaluée comme étant vraie, donc dans ce cas 1
  • + '' - ajoute une chaîne vide à l'expression entraînant la conversion du nombre en chaîne
  • + [] - évalue à 0

il en est de même

+(true) + '' + (0)
1 + '' + 0
"10"

Alors maintenant vous avez ça, essayez celui-ci:

_=$=+[],++_+''+$
13
Vlad Shlosberg

+ [] est évalué à 0 [...] puis additionné (+ opération), il convertit le contenu du tableau en représentation sous forme de chaîne composée d'éléments joints par une virgule.

Tout autre chose comme prendre un index de tableau (plus prioritaire que l'opération +) est ordinale et n'a rien d'intéressant.

7
Eskat0n

Les manières les plus courtes possibles d’évaluer une expression en "10" sans chiffres sont:

+!+[] + [+[]] // "10"

-~[] + [+[]] // "10"

// ========== Explication =========== \\

+!+[]: +[] est converti en 0. !0 est converti en true. +true est converti en 1 .-~[] = -(-1) qui est 1

[+[]]: +[] Convertit à 0. [0] est un tableau avec un seul élément 0.

Ensuite, JS évalue l'expression 1 + [0], donc Number + Array. La spécification ECMA fonctionne ensuite: L'opérateur + convertit les deux opérandes en chaîne en appelant les fonctions toString()/valueOf() à partir du prototype Object 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:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

Il existe une exception intéressante selon laquelle deux additions Objects entraînent NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"
4
Arman McHitarian
  1. Unary plus chaîne donnée convertit en nombre 
  2. Incrémenter l'opérateur donné chaîne convertit et incrémente de 1
  3. [] == ''. Chaîne vide
  4. + '' ou + [] évalue 0.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    
1
Praveen Vedanth

Etape par étape, + transforme la valeur en nombre et si vous ajoutez à un tableau vide +[]..., car il est vide et égal à 0, il 

Alors à partir de là, regardez dans votre code, c'est ++[[]][+[]]+[+[]]...

Et il y a plus entre eux ++[[]][+[]] + [+[]]

Donc, ces [+[]] renverront [0] car ils ont un tableau vide qui est converti en 0 dans l'autre tableau ...

Donc, comme vous pouvez l'imaginer, la première valeur est un tableau 2-dimensionnel avec un tableau à l'intérieur ... donc [[]][+[]] sera égal à [[]][0], ce qui renverra []...

Et à la fin, ++ convertissez-le et augmentez-le en 1...

Donc, vous pouvez imaginer, 1 + "0" sera "10"...

 Why does return the string “10”?

0
Alireza