Começo a ler JavaScript Patterns , alguns códigos me confundem.
var global = (function () {
return this || (1, eval)('this');
}());
Aqui estão minhas perguntas:
Q1:
(1, eval) === eval
?
Por que e como isso funciona?
P2: Por que não apenas
var global = (function () {
return this || eval('this');
}());
ou
var global = (function () {
return this;
}());
javascript
eval
Shawjia
fonte
fonte
Respostas:
A diferença entre
(1,eval)
e simpleseval
é que o primeiro é um valor e o último é um lvalue. Seria mais óbvio se fosse algum outro identificador:var x; x = 1; (1, x) = 1; // syntax error, of course!
Essa é
(1,eval)
uma expressão que produzeval
(como dizer,(true && eval)
ou(0 ? 0 : eval)
faria), mas não é uma referência aeval
.Por quê você se importa?
Bem, a Ecma especificação considera uma referência a
eval
ser uma "chamada eval direta", mas uma expressão que apenas produzeval
a ser um indireto - e chamadas eval indiretos são garantidos para executar no âmbito global.Coisas que ainda não sei:
this
de uma função no escopo global não pode produzir o objeto global?Mais algumas informações podem ser obtidas aqui .
EDITAR
Aparentemente, a resposta à minha primeira pergunta é "quase sempre". Um direto é
eval
executado a partir do escopo atual . Considere o seguinte código:var x = 'outer'; (function() { var x = 'inner'; eval('console.log("direct call: " + x)'); (1,eval)('console.log("indirect call: " + x)'); })();
Não surpreendentemente (heh-heh), isso imprime:
EDITAR
Após mais experiências, direi provisoriamente que
this
não pode ser definido comonull
ouundefined
. Ela pode ser configurada para valores Falsas (0, '', NaN, false), mas só muito deliberadamente.Vou dizer que sua fonte está sofrendo de uma inversão cranio-retal leve e reversível e pode querer passar uma semana programando em Haskell.
fonte
value
vslvalue
(bem, talvez na prática, mas não em palavras). Nem as regras de avaliação ES5 (não que eu deva razoavelmente precisar usareval
sempre). Obrigado!eval
tem muitas arestas afiadas e deve ser usado apenas como último recurso e então, com muito, muito cuidado.innerHtml
eval
e ser a parte MemberExpression de uma CallExpression e referir-se àeval
função padrão .eval
como destino de uma expressão de chamada é especial. Você afirma que o ECMA trata a referência aeval
especial, o que não é. É o posicionamento na expressão de chamada que é especial e que a expressão avalia para aeval
função padrão . Por exemplo,var eval = window.eval; eval('1');
ainda é um eval direto ewindow.eval('1')
não é, embora eval seja um lvalue neste caso também.O fragmento,
var global = (function () { return this || (1, eval)('this'); }());
será avaliado corretamente para o objeto global, mesmo no modo estrito. No modo não estrito, o valor de
this
é o objeto global, mas no modo estrito éundefined
. A expressão(1, eval)('this')
sempre será o objeto global. A razão para isso envolve as regras em torno dos versos indiretos diretoseval
. Chamadas diretas paraeval
têm o escopo do chamador e a stringthis
seria avaliada como o valor dethis
no encerramento. Os indiretoseval
são avaliados em escopo global como se fossem executados dentro de uma função em escopo global. Uma vez que essa função não é ela própria uma função de modo estrito, o objeto global é passado comothis
e, em seguida, a expressão é'this'
avaliada como o objeto global. A expressão(1, eval)
é apenas uma maneira elegante de forçar oeval
para ser indireto e retornar o objeto global.A1:
(1, eval)('this')
não é o mesmo poreval('this')
causa das regras especiais em torno do verso indireto chamadas diretas paraeval
.A2: O original funciona em modo estrito, as versões modificadas não.
fonte
Para Q1:
Acho que este é um bom exemplo de operador vírgula em JS. Gosto da explicação para o operador vírgula neste artigo: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
O operador vírgula avalia seus dois operandos (da esquerda para a direita) e retorna o valor do segundo operando.
Para Q2:
(1, eval)('this')
é considerada uma chamada de avaliação indireta, que no ES5 executa o código globalmente. Portanto, o resultado será o contexto global.Veja http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope
fonte
P1: Várias instruções javascript consecutivas separadas por uma vírgula assumem o valor da última instrução. Então:
(1, eval)
pega o valor do último que é uma referência de função para aeval()
função. Aparentemente faz isso desta forma para transformar aeval()
chamada em uma chamada de avaliação indireta que será avaliada no âmbito global no ES5. Detalhes explicados aqui .P2: Deve haver algum ambiente que não define um global
this
, mas defineeval('this')
. Essa é a única razão que posso pensar para isso.fonte
/eval\(/g
?eval
código é executado em seu próprio contexto, e não no contexto global ou no contexto envolvente. Uma maneira de contornar isso é referenciá-lo indiretamente como faz o código em questão.