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

85

Ich fange an, JavaScript-Muster zu lesen , einige Codes haben mich verwirrt.

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

Hier sind meine Fragen:

Q1:

(1, eval) === eval?

Warum und wie funktioniert es?

F2: Warum nicht einfach

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

oder

 var global = (function () {
    return this;
}());
Shawjia
quelle
Ich habe den Titel aktualisiert, da dies ein spezieller Fall ist. Auch Klammern für die bestimmte Art von Klammern : [] und {} sind völlig unterschiedlich :)

Antworten:

103

Der Unterschied zwischen (1,eval)und einfach alt evalist, dass Ersteres ein Wert ist und letzteres ist. Es wäre offensichtlicher, wenn es eine andere Kennung wäre:

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

Das ist (1,eval)ein Ausdruck, der nachgibt eval(genau wie sagen,(true && eval) oder (0 ? 0 : eval)würde), aber es ist kein Hinweis auf eval.

Warum kümmert es dich?

Nun, die Ecma-Spezifikation berücksichtigt einen Verweis aufeval einen „direkten eval Anruf“ zu sein, sondern einen Ausdruck, der lediglich ergibt evaleine indirekter zu sein - und indirekte eval Anrufe werden in globalem Bereich auszuführen garantiert.

Dinge, die ich immer noch nicht weiß:

  1. Unter welchen Umständen ruft eine direkte Bewertung auf nicht im globalen Bereich ausgeführt?
  2. Unter welchen Umständen kann die thisFunktion einer Funktion im globalen Bereich das globale Objekt nicht ergeben?

Weitere Informationen finden Sie hier .

BEARBEITEN

Anscheinend lautet die Antwort auf meine erste Frage "fast immer". Ein direkter evalwird aus dem aktuellen Bereich ausgeführt. Betrachten Sie den folgenden Code:

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

Es überrascht nicht (heh-heh), dies druckt aus:

direct call: inner
indirect call: outer

BEARBEITEN

Nach weiteren Experimenten werde ich vorläufig sagen, dass thisdies nicht auf nulloder eingestellt werden kann undefined. Es kann auf andere falsche Werte (0, '', NaN, false) gesetzt werden, aber nur sehr absichtlich.

Ich werde sagen, dass Ihre Quelle unter einer milden und reversiblen kranio-rektalen Inversion leidet und möglicherweise eine Woche lang in Haskell programmieren möchte.

Malvolio
quelle
3
Wow, wusste nicht das ganze valuevs lvalueDing ( na ja, in der Praxis vielleicht, aber nicht in Worten). Noch die ES5-Bewertungsregeln (nicht, dass ich sie vernünftigerweise evaljemals verwenden müsste ). Vielen Dank!
Stoive
Ja, evalhat viele böse scharfe Kanten und sollte nur als letztes Mittel und dann sehr, sehr vorsichtig verwendet werden.
Malvolio
Ich bin nur einmal auf eine gültige Verwendung innerHtml
gestoßen
1
lvalue hat wenig mit der Bestimmung der direkten Bewertung zu tun, da es normalerweise auf den Ausdruck verweist, der auf der linken Seite einer Zuweisung erscheinen kann, daher der Name lvalue im Gegensatz zu rvalue. Ein Aufruf von eval ist nur unter den in 15.1.2.1.1 der Spezifikation aufgeführten Bedingungen direkt, die besagen, dass der Bezeichner evalder MemberExpression-Teil einer CallExpression sein muss und sein muss und sich auf die Standardfunktion bezieht eval.
Chuckj
1
@ Malvolio Sie scheinen zu implizieren, dass Werte etwas mit direkter oder indirekter Bewertung zu tun haben, was sie nicht tun. Die Verwendung eines Bezeichners, der evalals Ziel eines Aufrufausdrucks bezeichnet wird, ist etwas Besonderes. Sie geben an, dass ECMA Verweise auf evalspezielle Ereignisse behandelt , die dies nicht tut. Es ist die Platzierung im Aufrufausdruck, die besonders ist und die der Ausdruck als Standardfunktion auswertet eval. Zum Beispiel var eval = window.eval; eval('1');ist es immer noch eine direkte Bewertung und auch window.eval('1')nicht, obwohl die Bewertung auch in diesem Fall ein Wert ist.
Chuckj
33

Das Fragment,

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

wird das globale Objekt auch im strengen Modus korrekt auswerten. Im nicht strengen Modus ist der Wert von thisdas globale Objekt, im strengen Modus jedoch undefined. Der Ausdruck (1, eval)('this')wird immer das globale Objekt sein. Der Grund dafür sind die Regeln für indirekte Verse direkt eval. Direkte Aufrufe von evalhaben den Gültigkeitsbereich des Aufrufers und die Zeichenfolge thiswürde den Wert von thisin der Schließung ergeben. Indirekte evals werden im globalen Bereich ausgewertet, als ob sie innerhalb einer Funktion im globalen Bereich ausgeführt würden. Da diese Funktion selbst keine Funktion im strengen Modus ist, wird das globale Objekt als übergeben thisund der Ausdruck wird dann 'this'an das globale Objekt ausgewertet. Der Ausdruck soll indirekt sein und das globale Objekt zurückgeben.(1, eval) ist nur eine ausgefallene Art, das zu erzwingeneval

A1: (1, eval)('this')ist nicht dasselbe wie eval('this')wegen der Sonderregeln für indirekte Verse, die direkt aufgerufen werden eval.

A2: Das Original arbeitet im strengen Modus, die geänderten Versionen nicht.

chuckj
quelle
12

Zu Q1:

Ich denke, dies ist ein gutes Beispiel für einen Kommaoperator in JS. Ich mag die Erklärung für Kommaoperatoren in diesem Artikel: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

Der Kommaoperator wertet beide Operanden aus (von links nach rechts) und gibt den Wert des zweiten Operanden zurück.

Zu Q2:

(1, eval)('this')wird als indirekter Auswertungsaufruf betrachtet, der in ES5 Code global ausführt. Das Ergebnis wird also der globale Kontext sein.

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

Grace Shao
quelle
7

Q1: Mehrere aufeinanderfolgende Javascript-Anweisungen, die durch ein Komma getrennt sind, nehmen den Wert der letzten Anweisung an. So:

(1, eval)Nimmt den Wert des letzten, der eine Funktionsreferenz auf die eval()Funktion ist. Dies geschieht anscheinend auf diese Weise, um den eval()Anruf in einen indirekten Bewertungsaufruf umzuwandeln, der in ES5 im globalen Bereich ausgewertet wird. Details hier erklärt .

F2: Es muss eine Umgebung geben, die kein globales definiert this, aber definiert eval('this'). Das ist der einzige Grund, an den ich denken kann.

jfriend00
quelle
Vielleicht versucht jemand, einem unzulässigen Check-in-Haken auszuweichen /eval\(/g?
Stoive
@Stoive - ja, ich habe mich auch über so etwas gewundert. Wenn es sich nicht um einen Check-In-Hook handelt, befindet sich irgendwo im Prozess ein Filter (möglicherweise Minimierung).
jfriend00
7
Es hat etwas mit dem strengen ES5-Modus zu tun. AFAIK im ES5-Strict-Modus wird jeder evalCode in seinem eigenen Kontext ausgeführt und nicht im globalen Kontext oder im umschließenden Kontext. Eine Möglichkeit, dies zu umgehen, besteht darin, indirekt darauf zu verweisen, wie es der betreffende Code tut.
Cristian Sanchez
1
Werfen Sie einen Blick auf " Globale Bewertung. Welche Möglichkeiten gibt es? ".
Saxoier
Meine Antwort wurde aktualisiert und enthält nun die Informationen von CDSanchez und @Saxoier. Vielen Dank.
jfriend00