Чтение Кайл Симпсона Вы не знаете JS: Scopes & Closures, он утверждает, что вы должны держаться подальше от обоих eval()
функции, и with
ключевым словом, потому что всякий раз, когда компилятор видит эти два (я перефразирую), это не выполнить некоторые оптимизации, связанные с лексической областью, и сохранить расположение идентификаторов, потому что эти ключевые слова могут потенциально изменить лексическую область, что делает оптимизацию компилятора неправильной (я предполагаю, что оптимизация - это что-то вроде компилятора, хранящего местоположение каждого идентификатора, так что он может предоставить значение идентификатора без его поиска, когда он запрашивается во время выполнения).
Теперь я понимаю, почему это произойдет, когда вы используете ключевое слово eval()
: ваш eval может оценивать ввод пользователя, а этот пользовательский ввод может быть объявлением новой переменной, которая затеняет другую переменную, к которой вы обращаетесь позже. выполняемая функция, если компилятор сохранил статическое местоположение, доступ вернет значение неправильного идентификатора (поскольку доступ должен был вернуть значение идентификатора, объявленного eval()
, но оно вернуло значение переменная, которая была сохранена компилятором для оптимизации поиска). Поэтому я просто предполагаю, что именно поэтому компилятор не выполняет зависящие от масштаба объекты, когда он видит код eval()
в вашем коде.
Но почему компилятор делать то же самое для with
ключевым словом? В книге говорится, что он делает это потому, что with
создает новую лексическую область во время выполнения, и он использует свойство объекта, переданный в качестве аргумента with
объявить новые идентификаторы. Я в буквальном смысле понятия не имею, что это значит, и мне очень трудно пытаться визуализировать все это, потому что все связанные с компилятором вещи в этой книге - это вся теория.
Я знаю, что я могу ошибиться, в этом случае, пожалуйста, любезно исправьте все мои недоразумения :)
Приведенная здесь оптимизация основана на этом факте: переменные, объявленные внутри функции, всегда могут быть определены посредством простого статического анализа кода (т.е. var
поиска объявлений var
/let
и function
) и набора объявленных переменных в пределах функция никогда не изменяется.
eval
нарушает это предположение, введя способность мутировать локальную привязку (путем ввода новых переменных в область функций во время выполнения). with
нарушением этого предположения, введя новое нелексическое связывание внутри функции, свойства которой вычисляются во время выполнения. Статический анализ кода не всегда может определить, от свойств with
объектом, так что анализатор не может определить, какие переменные существуют в пределах with
блоком. Важно отметить, что объект, поставляемый with
может меняться между выполнением функции, что означает, что набор переменных внутри этого лексического раздела функции никогда не может быть гарантированно согласован.
Рассмотрим простую функцию:
function foo() {
var a, b;
function c() { ... }
...
}
Все точки в foo
имеют три переменные локальной области видимости: a
, b
и c
. Оптимизатор может присоединить к функции, которая говорит: "Эта функция имеет три переменные: a
, b
и c
. Это никогда не изменится".
Теперь рассмотрим:
function bar(egg) {
var a, b;
function c() { ... }
with(egg) {
...
}
}
В блоке " with
неизвестно, какие переменные будут или не будут существовать. Если есть, a
b
или c
в with
, мы не знаем, до времени выполнения, если это относится к переменному bar
или которую создают with(egg)
лексической областью.
Чтобы показать полу-практический пример того, как это проблема, в конце рассмотрим:
function baz(egg) {
with(egg) {
return function() { return whereami; }
}
}
Когда выполняется внутренняя функция (например, bar({...})()
), механизм выполнения будет искать цепочку областей для поиска whereami
. Если оптимизатору было разрешено прикреплять постоянную заметку к baz
, тогда механизм выполнения сразу же узнает, что функция baz
whereami
для значения whereami
, потому что это будет гарантировано стать домом для whereami
(любой аналогично - именованная переменная вверх по цепочке областей видимости будет затенена ближайшим). Тем не менее, он не знает, если whereami
существует в baz
или нет, потому что это может быть условно создано содержимое egg
на конкретном пробеге bar
, создавшего, что внутренняя функция. Поэтому он должен проверять, и оптимизация не используется.
with
. Нам нужно проверять объект каждый раз, когда к переменной обращаются. Также это, конечно, предотвращает оптимизацию компилятора, которая переупорядочивает, дублирует или избегает доступа к переменным.
Возьмем следующий пример:
{
let a = 1; //stored at 123
{
let b = 2; //stored at 124
console.log(a/*123*/,b/*124*/);
}
}
И теперь это:
{
let a = 1;//stored at 123
with({a:3}){
console.log(a /*123 ??*/);
}
}
with
присутствовать в коде». На какой из этих вопросов ответили в этой статье?with
в 2017 году не в оптимизации, а в том, что мы пишем код строгого режима или ES6, в котором это даже не разрешено законом.