Pregunta ¿Por qué ++ [[]] [+ []] + [+ []] devuelve la cadena "10"?


Esto es válido y devuelve la cadena "10" en JavaScript (más ejemplos aquí)

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

¿Por qué? ¿Que está sucediendo aquí?


1439
2017-08-26 08:56


origen


Respuestas:


Si lo dividimos, el desorden es igual a:

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

En JavaScript, es cierto que +[] === 0. + convierte algo en un número, y en este caso se reducirá a +"" o 0 (ver detalles de especificación a continuación).

Por lo tanto, podemos simplificarlo (++ tiene precendencia sobre +)

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

Porque [[]][0] significa: obtener el primer elemento de [[]], es cierto que:

  • [[]][0] devuelve la matriz interna ([]) Debido a las referencias es incorrecto decir [[]][0] === [], pero llamemos a la matriz interna A para evitar la notación incorrecta.
  • ++[[]][0] == A + 1, ya que ++ significa 'incrementar en uno'.
  • ++[[]][0] === +(A + 1); en otras palabras, siempre será un número (+1 no devuelve necesariamente un número, mientras que ++ siempre lo hace, gracias a Tim Down por señalar esto).

De nuevo, podemos simplificar el desorden en algo más legible. Vamos a sustituir [] De vuelta por A:

+([] + 1)
+
[0]

En JavaScript, esto también es cierto: [] + 1 === "1", porque [] == "" (uniéndose a una matriz vacía), entonces:

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

Simplifiquemos aún más:

1
+
[0]

Además, esto es cierto en JavaScript: [0] == "0", porque se está uniendo a una matriz con un elemento. La unión concatenará los elementos separados por ,. Con un elemento, puede deducir que esta lógica dará como resultado el primer elemento en sí mismo.

Entonces, al final obtenemos (number + string = string):

1
+
"0"

=== "10" // Yay!

Detalles de especificación para +[]:

Esto es un laberinto, pero para hacer +[], primero se está convirtiendo en una cadena porque eso es lo + dice:

11.4.6 Unario + Operador

El operador unario + convierte su operando a tipo de Número.

La producción UnaryExpression: + UnaryExpression se evalúa de la siguiente manera:

  1. Deje que expr sea el resultado de evaluar UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber() dice:

Objeto

Aplicar los siguientes pasos:

  1. Deje que primValue sea ToPrimitive (argumento de entrada, cadena de sugerencias).

  2. Return ToString (primValue).

ToPrimitive() dice:

Objeto

Devuelve un valor predeterminado para el Objeto. El valor predeterminado de un objeto se recupera llamando al método interno [[DefaultValue]] del objeto, pasando la sugerencia opcional PreferredType. El comportamiento del método interno [[DefaultValue]] está definido por esta especificación para todos los objetos ECMAScript nativos en 8.12.8.

[[DefaultValue]] dice:

8.12.8 [[DefaultValue]] (sugerencia)

Cuando se llama al método interno [[DefaultValue]] de O con cadena de sugerencia, se realizan los siguientes pasos:

  1. Deje que toString sea el resultado de llamar al método interno [[Get]] del objeto O con el argumento "toString".

  2. Si IsCallable (toString) es verdadero, entonces,

a. Deje que str sea el resultado de llamar al método interno [[Call]] de toString, con O como este valor y una lista de argumentos vacía.

segundo. Si str es un valor primitivo, devuelve str.

los .toString de una matriz dice:

15.4.4.2 Array.prototype.toString ()

Cuando se llama al método toString, se realizan los siguientes pasos:

  1. Deje que matriz sea el resultado de llamar a ToObject en este valor.

  2. Deje que func sea el resultado de llamar al método interno de array [[Get]] con el argumento "join".

  3. Si IsCallable (func) es falso, entonces deja que func sea el método incorporado estándar Object.prototype.toString (15.2.4.2).

  4. Devuelve el resultado de llamar al método interno [[Call]] de func que proporciona matriz como este valor y una lista de argumentos vacía.

Asi que +[] se reduce a +"", porque [].join() === "".

Nuevamente, el + Se define como:

11.4.6 Unario + Operador

El operador unario + convierte su operando a tipo de Número.

La producción UnaryExpression: + UnaryExpression se evalúa de la siguiente manera:

  1. Deje que expr sea el resultado de evaluar UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber se define para "" como:

El MV de StringNumericLiteral ::: [vacío] es 0.

Asi que +"" === 0, y por lo tanto +[] === 0.


1874
2017-08-26 08:58



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

Entonces tenemos una concatenación de cadenas

1+[0].toString() = 10

99
2017-09-14 13:54



Lo siguiente es una adaptación de entrada en el blog respondiendo esta pregunta que publiqué mientras esta pregunta aún estaba cerrada. Los enlaces son para (una copia HTML de) la especificación ECMAScript 3, sigue siendo la línea de base para JavaScript en los navegadores web más utilizados actualmente.

Primero, un comentario: este tipo de expresión nunca se mostrará en ningún entorno de producción (sensato) y solo sirve como ejercicio para conocer qué tan bien conoce el lector los bordes sucios de JavaScript. El principio general de que los operadores de JavaScript implícitamente convierten entre tipos es útil, como lo son algunas de las conversiones comunes, pero gran parte del detalle en este caso no lo es.

La expresion ++[[]][+[]]+[+[]] inicialmente puede parecer bastante imponente y oscuro, pero en realidad es relativamente fácil descomponerlo en expresiones separadas. A continuación, simplemente he agregado paréntesis para mayor claridad; Puedo asegurarle que no cambian nada, pero si quiere verificarlo, no dude en leer sobre la operador de agrupación. Entonces, la expresión puede escribirse más claramente como

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

Rompiendo esto, podemos simplificar observando que +[] evalúa a 0. Para asegurarse de por qué esto es cierto, revise el operador unario + y sigue el sendero ligeramente tortuoso que termina con ToPrimitive convirtiendo la matriz vacía en una cadena vacía, que finalmente se convierte en 0 por Al numero. Ahora podemos sustituir 0 para cada instancia de +[]:

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

Simpler ya. Como para ++[[]][0], esa es una combinación de operador de incremento de prefijo (++), un matriz literal definir una matriz con un solo elemento que es en sí misma una matriz vacía ([[]]) y un acceso a la propiedad ([0]) invocó la matriz definida por la matriz literal.

Entonces, podemos simplificar [[]][0] para sólo [] y tenemos ++[], ¿derecho? De hecho, este no es el caso porque la evaluación ++[] arroja un error, que inicialmente puede parecer confuso. Sin embargo, un poco de pensamiento sobre la naturaleza de ++ lo deja claro: se usa para incrementar una variable (p. ++i) o una propiedad del objeto (p. ++obj.count) No solo evalúa a un valor, también almacena ese valor en alguna parte. En el caso de ++[], no tiene dónde colocar el nuevo valor (cualquiera que sea) porque no hay ninguna referencia a una propiedad o variable del objeto para actualizar. En términos de especificación, esto está cubierto por el interno PutValue operación, que es llamada por el operador de incremento de prefijo.

Entonces, ¿qué hace ++[[]][0] ¿hacer? Bueno, por una lógica similar a +[], la matriz interna se convierte en 0 y este valor se incrementa por 1 para darnos un valor final de 1. El valor de la propiedad 0 en la matriz externa se actualiza a 1 y toda la expresión se evalúa como 1.

Esto nos deja con

1 + [0]

... que es un uso simple de la operador adicional. Ambos operandos son los primeros convertido a primitivos y si cualquier valor primitivo es una cadena, se realiza la concatenación de cadena, de lo contrario se realiza la adición numérica. [0] convierte a "0", así que se usa la concatenación de cadenas, produciendo "10".

Como un último aparte, algo que puede no ser inmediatamente evidente es que anula cualquiera de los toString() o valueOf() métodos de Array.prototype cambiará el resultado de la expresión, porque ambos se verifican y se usan si están presentes al convertir un objeto en un valor primitivo. Por ejemplo, el siguiente

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

... produce "NaNfoo". Por qué sucede esto se deja como un ejercicio para el lector ...


57
2017-12-30 15:41



Vamos a hacerlo 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



Este se evalúa igual pero un poco más pequeño

+!![]+''+(+[])
  • [] - es una matriz convertida que se convierte en 0 cuando se agrega o se resta de ella, entonces + [] = 0
  • ! [] - evalúa como falso, por lo que !! [] evalúa como verdadero
  • + !! [] - convierte el verdadero en un valor numérico que se evalúa como verdadero, por lo que en este caso 1
  • + '' - agrega una cadena vacía a la expresión que causa que el número se convierta en cadena
  • + [] - evalúa a 0

por lo que se evalúa

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

Entonces, ahora que lo tienes, prueba este:

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

13
2017-08-26 08:58



+ [] evalúa a 0 [...] luego sumar (+ operación) con algo convierte el contenido de la matriz a su representación de cadena que consiste en elementos unidos con una coma.

Cualquier otra cosa, como tomar el índice de matriz (tener prioridad de grater más que + operación) es ordinal y no es nada interesante.


7
2017-12-30 08:10



Quizás las formas más cortas posibles de evaluar una expresión en "10" sin dígitos son:

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

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

// ========== Explicación ========== \\

+!+[] : +[] Convierte a 0. !0 convierte a true. +true convierte a 1. -~[] = -(-1) que es 1

[+[]] : +[] Convierte a 0. [0] es una matriz con un solo elemento 0.

Entonces JS evalúa el 1 + [0], por lo tanto Number + Array expresión. Entonces la especificación de ECMA funciona: + operador convierte ambos operandos en una cadena llamando al toString()/valueOf() funciones desde la base Object prototipo. Funciona como una función aditiva si ambos operandos de una expresión son solo números. El truco es que las matrices convierten fácilmente sus elementos en una representación de cadena concatenada.

Algunos ejemplos:

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

Hay una buena excepción que dos Objects resultados adicionales en NaN:

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

4



  1. Unario más cadena dada se convierte en número
  2. Incrementar el número de conversiones e incrementos de operador dados por 1
  3. [] == ''. Cuerda vacía
  4. + '' o + [] evalúa 0.

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

1