1881 votes

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

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 ?

541 votes

Commencez par comprendre que +[] convertit un tableau vide en 0 ... puis perdre un après-midi... ;)

17 votes

15 votes

Jetez un coup d'œil à wtfjs.com - il y a pas mal de choses comme ça avec des explications.

2264voto

pimvdb Points 66332

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 :

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

  2. Retourner ToNumber(GetValue(expr)).

ToNumber() dice:

Objet

Appliquez les étapes suivantes :

  1. Soit primValue ToPrimitive(input argument, hint String).

  2. 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 :

  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,

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 :

  1. Soit le tableau le résultat de l'appel à 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 faux, alors func est la méthode intégrée standard Object.prototype.toString (15.2.4.2).

  4. 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 :

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

  2. Retourner ToNumber(GetValue(expr)).

ToNumber est défini pour "" comme :

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

Alors +"" === 0 et donc +[] === 0 .

0 votes

Comment passe-t-on de ++[[]][0] a [] + 1 ?

0 votes

@Lie Ryan : [[]][0] est que l'emboîtement [] . ++ s'incrémente de un, et renvoie donc [] + 1 .

1 votes

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 !

135voto

Shef Points 21595
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Nous avons alors une concaténation de chaînes de caractères

1+[0].toString() = 10

8 votes

Ne serait-il pas plus clair d'écrire === plutôt que => ?

2 votes

@MateenUlhaq Peut-être que ce serait plus clair, mais ce ne serait pas complètement correct, car [+[]] === [0] vaut false en JS.

73voto

Tim Down Points 124501

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...

31voto

renatoluna Points 101

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"

16voto

Vlad Shlosberg Points 69

Celui-ci a la même évaluation mais est un peu plus petit.

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

donc il est évalué à

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

Maintenant que vous avez compris ça, essayez celui-là :

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

0 votes

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 votes

_=$=+[],++_+''+$ -> _=$=0,++_+''+$ -> _=0,$=0,++_+''+$ -> ++0+''+0 -> 1+''+0 -> '10' // Yei :v

6 votes

Celui-ci a la même évaluation, mais il est encore plus petit que le vôtre : "10"

Prograide.com

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.

Powered by:

X