(1, eval) ('this') против eval ('this') в JavaScript? - PullRequest
76 голосов
/ 02 февраля 2012

Я начинаю читать Шаблоны JavaScript , некоторые коды смутили меня.

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

Вот мои вопросы:

Q1:

(1, eval) === eval

Почему и как это работает?

Q2: Почему бы не просто

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

или

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

Ответы [ 4 ]

92 голосов
/ 02 февраля 2012

Разница между (1,eval) и простым старым eval заключается в том, что первое значение равно , а второе - lvalue. Это было бы более очевидно, если бы это был какой-то другой идентификатор:

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

То есть (1,eval) - это выражение, которое дает eval (как, скажем, (true && eval) или (0 ? 0 : eval)), но это не ссылка на eval.

Почему тебя это волнует?

Ну, спецификация Ecma рассматривает ссылку - eval как "прямой вызов eval", но выражение, которое просто возвращает eval как косвенный - и косвенные вызовы eval гарантированно выполняется в глобальном масштабе.

Вещи, которые я до сих пор не знаю:

  1. При каких обстоятельствах прямой вызов eval не выполняется в глобальной области видимости?
  2. При каких обстоятельствах this функции в глобальной области видимости не может привести к глобальному объекту?

Дополнительную информацию можно получить здесь .

РЕДАКТИРОВАТЬ

По-видимому, ответ на мой первый вопрос "почти всегда". Прямое eval выполняется из текущей области. Рассмотрим следующий код:

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

Не удивительно (хе-хе), это печатает:

direct call: inner
indirect call: outer

EDIT

После дальнейших экспериментов я временно скажу, что this нельзя установить на null или undefined. Можно установить другие ложные значения (0, '', NaN, false), но только очень сознательно.

Я хочу сказать, что ваш источник страдает от легкой и обратимой кранио-ректальной инверсии и может подумать о том, чтобы потратить неделю на программирование в Haskell.

30 голосов
/ 02 февраля 2012

Фрагмент,

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

будет правильно оценивать глобальный объект даже в строгом режиме. В нестрогом режиме значение this является глобальным объектом, но в строгом режиме это undefined. Выражение (1, eval)('this') всегда будет глобальным объектом. Причиной этого являются правила, касающиеся косвенных стихов direct eval. Прямые вызовы eval имеют область действия вызывающей стороны, и строка this будет иметь значение this в замыкании. Косвенные eval оцениваются в глобальной области видимости, как если бы они выполнялись внутри функции в глобальной области видимости. Поскольку эта функция сама по себе не является функцией строгого режима, глобальный объект передается как this, а затем выражение 'this' оценивается как глобальный объект. Выражение (1, eval) - это просто причудливый способ заставить eval быть косвенным и вернуть глобальный объект.

A1: (1, eval)('this') - это не то же самое, что eval('this') из-за особых правил, касающихся косвенных стихотворных прямых вызовов eval.

A2: оригинал работает в строгом режиме, измененные версии - нет.

11 голосов
/ 02 февраля 2012

К 1 кварталу:

Я думаю, что это хороший пример оператора запятой в JS. Мне нравится объяснение оператора запятой в этой статье: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

Оператор запятой оценивает оба своих операнда (слева направо) и возвращает значение второго операнда.

К Q2:

(1, eval)('this') рассматривается как косвенный вызов eval, который в ES5 выполняет код глобально. Таким образом, результатом будет глобальный контекст.

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

7 голосов
/ 02 февраля 2012

Q1: Несколько последовательных операторов javascript, разделенных запятой, принимают значение последнего оператора. Итак:

(1, eval) принимает значение последнего, которое является ссылкой на функцию eval(). По-видимому, он делает это таким образом, чтобы сделать вызов eval() в косвенный вызов eval, который будет оцениваться в глобальной области видимости в ES5. Подробности объяснили здесь .

Q2: Должна быть какая-то среда, которая не определяет глобальный this, но определяет eval('this'). Это единственная причина, по которой я могу думать об этом.

...