(1, eval) ('questo') vs eval ('questo') in JavaScript?


85

Comincio a leggere i pattern JavaScript , alcuni codici mi hanno confuso.

var global = (function () {
    return this || (1, eval)('this');
}());

Ecco le mie domande:

Q1:

(1, eval) === eval?

Perché e come funziona?

Q2: Perché non solo

var global = (function () {
    return this || eval('this');
}());

o

 var global = (function () {
    return this;
}());

Ho aggiornato il titolo perché questo è un caso specifico. Inoltre, le parentesi per il tipo specifico di parentesi : [] e {} sono completamente diverse :)

Risposte:


104

La differenza tra (1,eval)e plain old evalè che il primo è un valore e il secondo è un lvalue. Sarebbe più ovvio se fosse qualche altro identificatore:

var x;
x = 1;
(1, x) = 1; //  syntax error, of course!

Questa è (1,eval)un'espressione che produce eval(proprio come dire, (true && eval)o (0 ? 0 : eval)sarebbe), ma non è un riferimento a eval.

Perché ti interessi?

Ebbene, il Ecma spec considera un riferimento ad evalessere una "chiamata eval diretta", ma un'espressione che semplicemente cede evalad essere uno indiretto - e le chiamate eval indiretti sono garantiti da eseguire in ambito globale.

Cose che ancora non so:

  1. In quali circostanze una chiamata eval diretta non viene eseguita in ambito globale?
  2. In quale circostanza il thisdi una funzione a ambito globale non può produrre l'oggetto globale?

Ulteriori informazioni possono essere raccolte qui .

MODIFICARE

Apparentemente, la risposta alla mia prima domanda è "quasi sempre". Un direct evalviene eseguito dall'ambito corrente . Considera il codice seguente:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

Non sorprende (heh-heh), questo stampa:

direct call: inner
indirect call: outer

MODIFICARE

Dopo ulteriori esperimenti, dirò provvisoriamente che thisnon può essere impostato su nullo undefined. Può essere impostato su altri valori falsi (0, ", NaN, false), ma solo molto deliberatamente.

Dirò che la tua fonte soffre di una lieve e reversibile inversione cranio-rettale e potresti prendere in considerazione l'idea di trascorrere una settimana a programmare ad Haskell.


3
Wow, non sapevo tutto il valuevs lvalue(beh, in pratica forse, ma non a parole). Né le regole di valutazione ES5 (non che dovrei ragionevolmente aver bisogno di usare evalmai). Grazie!
Stoive

Sì, evalha molti brutti bordi taglienti e dovrebbe essere usato solo come ultima risorsa e poi, molto, molto attentamente.
Malvolio

Solo una volta mi sono imbattuto in un uso valido - valutando un tag script che era stato aggiunto al DOM tramiteinnerHtml
Stoive

1
lvalue ha poco a che fare con la determinazione dell'eval diretto poiché di solito si riferisce all'espressione che può apparire sul lato sinistro di un compito, da cui il nome lvalue in opposizione a rvalue. Una chiamata a eval è diretta solo nelle condizioni elencate in 15.1.2.1.1 della specifica che dice che l'identificatore deve essere evaled essere la parte MemberExpression di una CallExpression e fare riferimento alla evalfunzione standard .
chuckj

1
@Malvolio Sembra che tu stia insinuando che i valori abbiano qualcosa a che fare con la valutazione diretta e indiretta che non hanno. L'uso di un identificatore chiamato evalcome destinazione di un'espressione di chiamata è speciale. Affermi che ECMA tratta il riferimento a evalspeciali che non fa. È il posizionamento nell'espressione di chiamata che è speciale e l'espressione valuta la evalfunzione standard . Ad esempio, var eval = window.eval; eval('1');è ancora un eval diretto e window.eval('1')non è anche se eval è un lvalue anche in questo caso.
chuckj

33

Il frammento,

var global = (function () {  
    return this || (1, eval)('this');  
}());  

valuterà correttamente l'oggetto globale anche in modalità rigorosa. In modalità non rigorosa il valore di thisè l'oggetto globale ma in modalità rigorosa lo è undefined. L'espressione (1, eval)('this')sarà sempre l'oggetto globale. La ragione di ciò coinvolge le regole sui versi indiretti diretti eval. Le chiamate dirette a evalhanno l'ambito del chiamante e la stringa thisrestituirà il valore di thisnella chiusura. I messaggi indiretti vengono evalvalutati nell'ambito globale come se fossero eseguiti all'interno di una funzione nell'ambito globale. Poiché quella funzione non è di per sé una funzione in modalità rigorosa, l'oggetto globale viene passato come thise quindi l'espressione 'this'restituisce l'oggetto globale. L'espressione (1, eval)è solo un modo elegante per forzare ileval essere indiretto e restituire l'oggetto globale.

A1: (1, eval)('this')non è la stessa a eval('this')causa delle regole speciali sulle chiamate dirette verso indiretto a eval.

A2: L'originale funziona in modalità rigorosa, le versioni modificate no.


12

A Q1:

Penso che questo sia un buon esempio di operatore virgola in JS. Mi piace la spiegazione dell'operatore virgola in questo articolo: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

L'operatore virgola valuta entrambi i suoi operandi (da sinistra a destra) e restituisce il valore del secondo operando.

A Q2:

(1, eval)('this')è considerata una chiamata eval indiretta, che in ES5 esegue il codice a livello globale. Quindi il risultato sarà il contesto globale.

Vedi http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope


7

D1: Più istruzioni javascript consecutive separate da una virgola assumono il valore dell'ultima istruzione. Così:

(1, eval)prende il valore dell'ultimo che è un riferimento alla eval()funzione. Apparentemente lo fa in questo modo per trasformare la eval()chiamata in una chiamata di valutazione indiretta che verrà valutata nell'ambito globale in ES5. Dettagli spiegati qui .

D2: Deve essere presente un ambiente che non definisce un globale this, ma definisce eval('this'). Questa è l'unica ragione a cui riesco a pensare per questo.


Forse qualcuno sta cercando di schivare un gancio del check-in che non consente /eval\(/g?
Stoive

@ Stoive - sì, mi chiedevo anche qualcosa del genere. Se non è un hook di check-in, qualche filtro da qualche parte nel processo (forse minimizzazione).
jfriend00

7
Ha qualcosa a che fare con la modalità rigorosa di ES5. Per quanto ne so, in modalità rigorosa ES5 qualsiasi evalcodice 'd viene eseguito nel proprio contesto piuttosto che nel contesto globale o nel contesto che lo racchiude. Un modo per aggirare questo problema è fare riferimento indirettamente ad esso come fa il codice in questione.
Cristian Sanchez


Aggiornata la mia risposta per includere le informazioni di CDSanchez e @Saxoier. Grazie.
jfriend00
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.