Isso é válido e retorna a string "10"em JavaScript ( mais exemplos aqui ):
console.log(++[[]][+[]]+[+[]])
Por quê? O que esta acontecendo aqui?
Isso é válido e retorna a string "10"em JavaScript ( mais exemplos aqui ):
console.log(++[[]][+[]]+[+[]])
Por quê? O que esta acontecendo aqui?
Respostas:
Se dividirmos, a bagunça é igual a:
++[[]][+[]]
+
[+[]]
Em JavaScript, é verdade isso +[] === 0. +converte algo em um número e, nesse caso, será reduzido a +""ou 0(consulte os detalhes das especificações abaixo).
Portanto, podemos simplificá-lo ( ++tem precedência sobre +):
++[[]][0]
+
[0]
Porque [[]][0]significa: obter o primeiro elemento [[]], é verdade que:
[[]][0]retorna a matriz interna ( []). Devido às referências, é errado dizer [[]][0] === [], mas vamos chamar a matriz interna Apara evitar a notação errada.
++antes de seu operando significa "incrementar por um e retornar o resultado incrementado". Então ++[[]][0]é equivalente a Number(A) + 1(ou +A + 1).
Novamente, podemos simplificar a bagunça em algo mais legível. Vamos substituir []novamente por A:
(+[] + 1)
+
[0]
Antes de +[]poder coagir a matriz no número 0, ela precisa ser coagida em uma cadeia primeiro, ou seja "", novamente. Por fim, 1é adicionado, o que resulta em 1.
(+[] + 1) === (+"" + 1)(+"" + 1) === (0 + 1)(0 + 1) === 1Vamos simplificar ainda mais:
1
+
[0]
Além disso, isso é verdade no JavaScript: [0] == "0"porque está juntando uma matriz com um elemento. A junção concatenará os elementos separados por ,. Com um elemento, você pode deduzir que essa lógica resultará no próprio elemento.
Nesse caso, +vê dois operandos: um número e uma matriz. Agora, ele está tentando coagir os dois no mesmo tipo. Primeiro, o array é coagido na string "0"; em seguida, o número é coagido em uma string ( "1"). Number +String ===String .
"1" + "0" === "10" // Yay!
Detalhes da especificação para +[]:
Este é um labirinto, mas, para fazer +[], primeiro ele está sendo convertido em uma string, porque é o que +diz:
11.4.6 Unário + Operador
O operador unary + converte seu operando no tipo Number.
A produção UnaryExpression: + UnaryExpression é avaliada da seguinte maneira:
Expr seja o resultado da avaliação da UnaryExpression.
Retornar ToNumber (GetValue (expr)).
ToNumber() diz:
Objeto
Aplique as seguintes etapas:
Deixe primValue ser ToPrimitive (argumento de entrada, dica String).
Retorne ToString (primValue).
ToPrimitive() diz:
Objeto
Retorne um valor padrão para o objeto. O valor padrão de um objeto é recuperado chamando o método interno [[DefaultValue]] do objeto, passando a dica opcional PreferredType. O comportamento do método interno [[DefaultValue]] é definido por esta especificação para todos os objetos nativos do ECMAScript na versão 8.12.8.
[[DefaultValue]] diz:
8.12.8 [[DefaultValue]] (dica)
Quando o método interno [[DefaultValue]] de O é chamado com a dica String, são executadas as seguintes etapas:
Permita que toString seja o resultado da chamada do método interno [[Get]] do objeto O com o argumento "toString".
Se IsCallable (toString) for verdadeiro,
uma. Seja str o resultado da chamada do método interno [[Call]] de toString, com O como este valor e uma lista de argumentos vazia.
b. Se str for um valor primitivo, retorne str.
O .toStringde uma matriz diz:
15.4.4.2 Array.prototype.toString ()
Quando o método toString é chamado, as seguintes etapas são executadas:
Deixe a matriz ser o resultado da chamada de ToObject neste valor.
Seja func como resultado da chamada do método interno [[Get]] da matriz com o argumento "join".
Se IsCallable (func) for false, permita que func seja o método interno padrão Object.prototype.toString (15.2.4.2).
Retorne o resultado da chamada do método interno [[Call]] da função de fornecimento de funções como o valor this e uma lista de argumentos vazia.
Então tudo +[]se resume a +"", porque [].join() === "".
Novamente, o +é definido como:
11.4.6 Unário + Operador
O operador unary + converte seu operando no tipo Number.
A produção UnaryExpression: + UnaryExpression é avaliada da seguinte maneira:
Expr seja o resultado da avaliação da UnaryExpression.
Retornar ToNumber (GetValue (expr)).
ToNumberé definido ""como:
O MV do StringNumericLiteral ::: [vazio] é 0.
Então +"" === 0e assim +[] === 0.
truese o valor e o tipo forem iguais. 0 == ""retorna true(o mesmo após a conversão de tipo), mas 0 === ""é false(não é o mesmo tipo).
1 + [0], não "1" + [0], porque o ++operador prefix ( ) sempre retorna um número. Veja bclary.com/2004/11/07/#a-11.4.4
++[[]][0]retorna de fato 1, mas ++[]gera um erro. Isso é notável porque parece que ++[[]][0]tudo se resume a ++[]. Você talvez tenha alguma idéia de por que ++[]lança um erro ++[[]][0]e não o faz?
PutValuechamada (na terminologia ES3, 8.7.2) na operação de prefixo. PutValuerequer uma referência, enquanto []uma expressão por si só não produz uma referência. Uma expressão que contém uma referência variável (dizer que tinha previamente definido var a = [], em seguida, ++atrabalha) ou acesso a propriedade de um objeto (como [[]][0]) produz uma referência. Em termos mais simples, o operador de prefixo não apenas produz um valor, mas também precisa de um lugar para colocar esse valor.
var a = []; ++a, aé 1. Após a execução ++[[]][0], a matriz criada pela [[]]expressão agora contém apenas o número 1 no índice 0. ++requer uma Referência para fazer isso.
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
Então nós temos uma concatenação de strings
1+[0].toString() = 10
===do que =>?
O seguinte é adaptado de uma postagem de blog que respondeu a essa pergunta que eu postei enquanto ela ainda estava fechada. Os links são para (uma cópia em HTML) da especificação do ECMAScript 3, ainda a linha de base do JavaScript nos navegadores da web mais usados atualmente.
Primeiro, um comentário: esse tipo de expressão nunca será exibido em qualquer ambiente de produção (sadio) e é de alguma utilidade como um exercício de quão bem o leitor conhece as arestas sujas do JavaScript. O princípio geral de que os operadores JavaScript convertem implicitamente entre tipos é útil, assim como algumas conversões comuns, mas muitos dos detalhes nesse caso não são.
A expressão ++[[]][+[]]+[+[]]pode inicialmente parecer bastante imponente e obscura, mas na verdade é relativamente fácil dividir-se em expressões separadas. Abaixo, simplesmente adicionei parênteses para maior clareza; Posso garantir que eles não mudam nada, mas se você quiser verificar isso, fique à vontade para ler sobre o operador de agrupamento . Portanto, a expressão pode ser escrita mais claramente como
( ++[[]][+[]] ) + ( [+[]] )
Por fim, podemos simplificar observando o que é +[]avaliado como 0. Para saber por que isso é verdade, confira o operador unary + e siga a trilha um pouco tortuosa que acaba com o ToPrimitive convertendo a matriz vazia em uma string vazia, que é finalmente convertida 0pelo ToNumber . Agora podemos substituir 0cada instância de +[]:
( ++[[]][0] ) + [0]
Já é mais simples. Quanto a ++[[]][0]isso, é uma combinação do operador de incremento de prefixo ( ++), um literal de matriz que define uma matriz com um único elemento que é uma matriz vazia ( [[]]) e um acessador de propriedade ( [0]) chamado na matriz definida pelo literal da matriz.
Então, podemos simplificar [[]][0]para justamente []e temos ++[], certo? De fato, esse não é o caso, porque a avaliação ++[]gera um erro, que pode inicialmente parecer confuso. No entanto, um pouco de reflexão sobre a natureza de ++deixa isso claro: é usado para incrementar uma variável (por exemplo ++i) ou uma propriedade de objeto (por exemplo ++obj.count). Além de avaliar um valor, ele também armazena esse valor em algum lugar. No caso de ++[], ele não tem onde colocar o novo valor (seja ele qual for), porque não há referência a uma propriedade ou variável de objeto a ser atualizada. Em termos de especificação, isso é coberto pela operação PutValue interna , chamada pelo operador de incremento de prefixo.
Então, o que ++[[]][0]faz? Bem, por uma lógica semelhante à +[], a matriz interna é convertida em 0e esse valor é incrementado por 1para nos dar um valor final de 1. O valor da propriedade 0na matriz externa é atualizado 1e a expressão inteira é avaliada em 1.
Isso nos deixa com
1 + [0]
... que é um uso simples do operador de adição . Ambos os operandos são primeiro convertidos em primitivos e se um dos valores primitivos for uma sequência, a concatenação da sequência será executada, caso contrário, a adição numérica será realizada. [0]converte em "0", então a concatenação de string é usada, produzindo "10".
Como um aspecto final, algo que pode não ser imediatamente aparente é que a substituição de um dos métodos toString()ou alterará o resultado da expressão, porque ambos são verificados e usados se presentes ao converter um objeto em um valor primitivo. Por exemplo, o seguintevalueOf()Array.prototype
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... produz "NaNfoo". Por que isso acontece é deixado como um exercício para o leitor ...
Vamos simplificar:
++[[]][+[]]+[+[]] = "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"
Este avalia para o mesmo, mas um pouco menor
+!![]+''+(+[])
então é avaliado como
+(true) + '' + (0)
1 + '' + 0
"10"
Então agora você conseguiu, tente este:
_=$=+[],++_+''+$
"10"
+ [] é avaliado como 0 e, em seguida, somado (+ operação) com qualquer coisa converte o conteúdo da matriz em sua representação de string, consistindo em elementos unidos por vírgula.
Qualquer outra coisa como pegar o índice da matriz (tem prioridade maior que + operação) é ordinal e não é nada interessante.
Talvez as formas mais curtas possíveis de avaliar uma expressão em "10" sem dígitos sejam:
+!+[] + [+[]] // "10"
-~[] + [+[]] // "10"
// ========== Explicação ========== \\
+!+[]: +[]Converte em 0. !0converte em true. +trueconverte para 1.
-~[]= -(-1)que é 1
[+[]]: +[]Converte em 0. [0]é uma matriz com um único elemento 0.
Então JS avalia a expressão 1 + [0], portanto Number + Array. Em seguida, a especificação ECMA funciona: o +operador converte os dois operandos em uma string chamando as toString()/valueOf()funções do Objectprotótipo de base . Funciona como uma função aditiva se os dois operandos de uma expressão forem apenas números. O truque é que as matrizes convertem facilmente seus elementos em uma representação de string concatenada.
Alguns exemplos:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
Há uma boa exceção que Objectsresulta em duas adições NaN:
[] + [] // ""
[1] + [2] // "12"
{} + {} // NaN
{a:1} + {b:2} // NaN
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
+ '' ou + [] avalia 0.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10[] é equivalente a . Primeiro, o elemento é extraído e depois convertido por . ""++
Passo a passo disso, +vire valor para um número e se você adicionar a uma matriz vazia +[]... como ela está vazia e é igual a 0, ela será
Então, a partir daí, agora observe seu código, é ++[[]][+[]]+[+[]]...
E há mais entre eles ++[[]][+[]]+[+[]]
Então, eles [+[]]retornarão [0]como eles têm uma matriz vazia que é convertida para 0dentro da outra matriz ...
Então, como imaginamos, o primeiro valor é uma matriz bidimensional com uma matriz dentro ... então [[]][+[]]será igual ao [[]][0]que retornará []...
E no final ++converta e aumente para 1...
Então você pode imaginar, 1+ "0"será "10"...
+[]lança uma matriz vazia para0... depois desperdice uma tarde ...;)