Косвенный вызов eval в строгом режиме


Я понимаю, о том, как eval() работает в нестрогих контекстах, однако в случае использования eval() в строгом режиме совершенно сбил меня с толку. Когда eval() вызывается непосредственно в глобальной области видимости, переменные хранятся внутри нового eval() объем:

'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined

однако, если я выполнить косвенные вызов eval() в глобальном масштабе (должно быть то же самое, верно?), он действует так, как будто он не находится в строгом режиме (если вы мне не верите, см. это JSFiddle):

'use strict';
(0, eval)('var a = 1;'); // indirect call to eval
console.log(a); // 1???

если вы не понимаете, что (0, eval) Не вижу почему используется главная страница google (0, obj.func) (args) синтаксис?.

по крайней мере, в моем понимании как eval() предполагается работать в строгом режиме, он предназначен для (независимо от того, является ли eval() вызывается прямо или косвенно) создайте новую область для переменных, определенных в eval() вызова, однако это не похоже, здесь. В спецификации говорится следующее:

10.4.2 Ввод Кода Eval

следующие шаги выполняются, когда элемент управления входит в контекст выполнения для кода eval:

  1. если нет контекста вызова или если код eval не оценивается прямым вызовом ( 15.1.2.1.1) к функции eval затем,

    а. Инициализировать выполнение контекст, как если бы это был глобальный контекст выполнения с использованием кода eval как C как описано в 10.4.1.1.

  2. еще,

    а. Выберите это биндинг к тому же значению, что и это биндинг контекста выполнения вызова.
    б. Выберите LexicalEnvironment к тому же значению, что и LexicalEnvironment выполнения вызова контекст.
    С. Выберите переменная среда к тому же значению, что и переменная среда контекста выполнения вызова.

  3. если код eval строгий код, потом

    а. Пусть strictVarEnv быть результатом вызова NewDeclarativeEnvironment мимо LexicalEnvironment как аргумент.
    б. Установить LexicalEnvironment до strictVarEnv.
    С. Выберите переменная среда до strictVarEnv.

  4. выполнить Объявление Привязки Экземпляра как описано в 10.5 С помощью кода eval.

это относится ко всем основным браузерам, включая (но не ограничиваясь) Internet Explorer 10, Chrome 30 и Firefox 24 - поскольку все они имеют одинаковое поведение, я не думаю, что это ошибка. Разве они оба не должны делать одно и то же, а если нет, то почему это так?

Примечание: пожалуйста не говори мне не использовать eval() (да, я знаю "опасности" использования eval()) - Я просто хочу понять логику этого, которая совершенно сбивает меня с толку.

1   51   2013-10-14 14:14:06

1 ответ:

tl; dr

второй (0, eval)('var a = 1;'); case на самом деле не является прямым вызовом.

вы можете увидеть это более широко в:

(function(){ "use strict"
    var x = eval;
    x("var y = 10"); // look at me all indirect
    window.y;// 10
    eval("var y = 11");
    window.y;// still 10, direct call in strict mode gets a new context
})();

проблема может быть замечена в:

если код eval является строгим кодом, то (me: fix context)

но строгий код eval определяется как:

код Eval является строгим кодом eval, если он начинается с пролога директивы, содержащего директиву Use Strict, или если вызов eval-это прямой вызов.

С момента вызова не является прямой, код eval-это не строгий свод оценки и выполнения на глобальном уровне.


прежде всего большой вопрос.

"код Eval" является более общим, чем прямой или косвенный вызов eval.

давайте проверим точную спецификацию на eval функции

15.1.2.1 eval (x)

когда функция eval вызывается с одним аргументом x, выполняются следующие действия:

  1. если тип(x) не является строкой, верните x.

  2. пусть prog-это код ECMAScript, который является результатом разбора x как программы. Если синтаксический анализ не удается, бросьте исключение SyntaxError (но см. Также пункт 16).

  3. пусть evalCtx будет результатом создание нового контекста выполнения (10.4.2) для кода eval еда.

  4. пусть результат будет результатом оценки программы prog.

  5. выйдите из запущенного контекста выполнения evalCtx, восстановив предыдущий контекст выполнения. ...

Итак, давайте рассмотрим, что говорит нам 10.4.2, вы процитировали это-в частности, давайте посмотрим на первое предложение:

если нет вызывающего контекста или если код eval не оценивается прямым затем вызовите функцию eval (15.1.2.1.1)... Инициализировать контекст выполнения, как если бы это был глобальный контекст исполнения

так что же такое прямой вызов?

прямой вызов функции eval-это вызов, который выражается как выражение вызова, удовлетворяющее следующим двум условиям:

ссылка, которая является результатом оценки MemberExpression в CallExpression имеет запись среды в качестве базового значения и его имя ссылки - "eval".

результатом вызова абстрактной операции GetValue с этой ссылкой в качестве аргумента является стандартная встроенная функция, определенная в 15.1.2.1.

Итак, что такое MemberExpression в обоих случаях?

на eval('var a = 1;'); действительно, результат оценки имеет ссылочное имя eval, а вызов GetValue разрешение на нем возвращает встроенную функцию.

на (0, eval)('var a = 1;'); результат оценки членов выражение не есть имя ссылки eval. (Однако он разрешает встроенную функцию на GetValue).

что такое ссылочные имена в любом случае?

раздел 8.7 в спец говорит нам:

Ссылка-это разрешенная привязка имени. Ссылка состоит из трех компонентов: базового значения, имени ссылки и флага строгой ссылки с логическим значением. Базовое значение либо не определено, либо является объектом, логическое значение, строка, число или запись среды (10.2.1). Базовое значение undefined указывает, что ссылка не может быть разрешен в силу. Имя ссылки-это строка.

это требует от нас заглянуть в GetReferencedName:

GetReferencedName (V). Возвращает компонент ссылочного имени ссылки V.

Итак, пока выражение (0,eval) === eval верно, при оценке функции, это на самом деле косвенный вызов из-за именования.

могу я предложить конструктор :)?