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


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

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

Pourquoi? Que se passe-t-il ici?


1439
2017-08-26 08:56


origine


Réponses:


Si nous le divisons, le désordre est égal à:

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

En JavaScript, il est vrai que +[] === 0. + convertit quelque chose en un nombre, et dans ce cas, il descendra à +"" ou 0 (voir les spécifications ci-dessous).

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

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

Car [[]][0] signifie: obtenir le premier élément de [[]], c'est vrai que:

  • [[]][0] retourne le tableau interne ([]). En raison de références, il est faux de dire [[]][0] === [], mais appelons le tableau interne A pour éviter la mauvaise notation.
  • ++[[]][0] == A + 1, depuis ++ signifie «augmenter de un».
  • ++[[]][0] === +(A + 1); en d'autres termes, ce sera toujours un nombre (+1 ne renvoie pas nécessairement un nombre, alors que ++ fait toujours - merci à Tim Down de le signaler).

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

+([] + 1)
+
[0]

En JavaScript, ceci est également vrai: [] + 1 === "1", car [] == "" (rejoindre un tableau vide), donc:

  • +([] + 1) === +("" + 1), et
  • +("" + 1) === +("1"), et
  • +("1") === 1

Simplifions-le encore plus:

1
+
[0]

Aussi, c'est vrai en JavaScript: [0] == "0", parce qu'il rejoint un tableau avec un élément. Rejoindre concaténera les éléments séparés par ,. Avec un élément, vous pouvez déduire que cette logique aboutira au premier élément lui-même.

Donc, à la fin, nous obtenons (nombre + chaîne = chaîne):

1
+
"0"

=== "10" // Yay!

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

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

11.4.6 Opérateur Unary +

L'opérateur unary + convertit son opérande en type Number.

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

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

  2. Renvoie ToNumber (GetValue (expr)).

ToNumber() dit:

Objet

Appliquez les étapes suivantes:

  1. Laisser primValue être ToPrimitive (argument d'entrée, hint String).

  2. Retour à 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 passant 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 de 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 effectuées:

  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 vrai alors,

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

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. Laissez array être le résultat de l'appel de ToObject sur cette valeur.

  2. Soit 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, alors laissez func être la 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]] du tableau fournissant func en tant que cette valeur et d'une liste d'arguments vide.

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

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

11.4.6 Opérateur Unary +

L'opérateur unary + convertit son opérande en type Number.

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

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

  2. Renvoie ToNumber (GetValue (expr)).

ToNumber est défini pour "" comme:

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

Alors +"" === 0, Et ainsi +[] === 0.


1874
2017-08-26 08:58



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

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

1+[0].toString() = 10

99
2017-09-14 13:54



Ce qui suit est adapté d'un article de blog répondre à cette question que j'ai posté alors que cette question était encore fermée. Les liens sont à (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 genre d'expression ne va jamais apparaître dans un environnement de production (sain d'esprit) et ne sert à rien d'un exercice sur la façon dont le lecteur connaît les bords sales de JavaScript. Le principe général selon lequel les opérateurs JavaScript convertissent implicitement entre types est utile, tout comme certaines des conversions courantes, mais une grande partie des détails dans ce cas ne l'est pas.

L'expression ++[[]][+[]]+[+[]] peut d'abord sembler plutôt imposant et obscur, mais est en fait relativement facile à décomposer en différentes expressions. 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, alors n'hésitez pas à lire sur le opérateur de groupement. Ainsi, l'expression peut être plus clairement écrite comme

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

Briser cela, nous pouvons simplifier en observant que +[] évalue à 0. Pour vous satisfaire pourquoi cela est vrai, consultez le opérateur unaire + et suivez le sentier légèrement tortueux qui finit avec ToPrimitive convertir le tableau vide en une chaîne vide, qui est ensuite convertie en 0 par ToNumber. Nous pouvons maintenant remplacer 0 pour chaque instance de +[]:

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

Plus simple déjà. Pour ce qui est de ++[[]][0], c'est une combinaison de opérateur d'incrémentation de 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ée sur le tableau défini par le tableau literal.

Donc, nous pouvons simplifier [[]][0] pour juste [] et nous avons ++[], droite? En fait, ce n'est pas le cas parce que l'évaluation ++[] renvoie une erreur, qui peut sembler confuse au départ. Cependant, un peu de réflexion sur la nature de ++ Cela est clair: il est utilisé pour incrémenter une variable (par ex. ++i) ou une propriété d'objet (par ex. ++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ù placer 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 de spécifications, cela est couvert par le interne PutValue opération, qui est appelée par l'opérateur d'incrémentation de préfixe.

Alors, qu'est-ce que ++[[]][0] faire? 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 mis à jour pour 1 et l'expression entière évalue à 1.

Cela nous laisse avec

1 + [0]

... qui est un simple usage de la opérateur d'addition. Les deux opérandes sont d'abord converti 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 une addition numérique est effectuée. [0] convertit en "0", donc la concaténation de chaîne est utilisée, produisant "10".

En dernier lieu, quelque chose qui peut ne pas être immédiatement évident est que l'un ou l'autre toString() ou valueOf() Méthodes de Array.prototype changera le résultat de l'expression, car les deux sont vérifiés et utilisés si présents lors de la conversion d'un objet en une valeur primitive. Par exemple, le suivant

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

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


57
2017-12-30 15:41



Faisons simple:

++[[]][+[]]+[+[]] = "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"

21
2017-12-28 23:13



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

+!![]+''+(+[])
  • [] - est un tableau converti à 0 lorsque vous ajoutez ou soustrayez, donc + [] = 0
  • ! [] - évalue à faux, donc d'où !! [] évalue à vrai
  • + !! [] - convertit le vrai à une valeur numérique qui évalue à vrai, donc dans ce cas 1
  • + '' - ajoute une chaîne vide à l'expression provoquant la conversion du nombre en chaîne
  • + [] - évalue à 0

est donc évalue à

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

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

_=$=+[],++_+''+$

13
2017-08-26 08:58



+ [] évalue à 0 [...] puis en sommant (+ opération) avec tout convertit le contenu du tableau en sa représentation sous forme de chaîne composée d'éléments joints avec une virgule.

Quelque chose d'autre comme prendre l'indice de tableau (avoir une priorité supérieure à + opération) est ordinale et n'a rien d'intéressant.


7
2017-12-30 08:10



Les moyens les plus courts pour évaluer une expression en "10" sans chiffres sont peut-être les suivants:

+!+[] + [+[]] // "dix"

-~[] + [+[]]  // "dix"

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

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

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

Ensuite, JS évalue le 1 + [0], Ainsi Number + Array expression. Alors la spécification ECMA fonctionne: + l'opérateur convertit les deux opérandes en une chaîne en appelant le toString()/valueOf() fonctions de la base Object prototype. Il fonctionne comme une fonction additive si les deux opérandes d'une expression sont des nombres seulement. L'astuce est que les tableaux convertissent facilement leurs éléments en une représentation 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 y a une belle exception que deux Objects résultats supplémentaires dans NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

4



  1. Unary plus une chaîne donnée convertit en nombre
  2. L'opérateur d'incrémentation chaîne donnée 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