Подтвердить что ты не робот

Косвенный вызов 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() call, однако, похоже, это не так. В спецификации указано следующее:

10.4.2 Ввод Eval Code

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

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

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

4b9b3361

Ответ 1

TL;DR

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

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

(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 code - это строгий код eval, если он начинается с директивы Prologue, содержащей директиву Strict, или если вызов eval является прямым вызовом.

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


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

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

Проверить точную спецификацию для функции eval

15.1.2.1 eval (x)

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

  • Если Type (x) не является строкой, верните x.

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

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

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

  • Закройте исполняемый контекст выполнения evalCtx, восстановив предыдущий контекст выполнения....

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

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

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

Прямой вызов функции 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 в спецификации сообщает нам:

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

Это требует от нас поискать GetReferencedName:

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

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

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