Эксперты JavaScript: почему `with` сводит на нет оптимизацию компилятора, связанную с областью действия
Читая книгу Кайла Симпсона "Вы не знаете": "Области применения и замыкания", он утверждает, что вам следует держаться подальше от eval()
функция и with
ключевое слово, потому что всякий раз, когда компилятор видит эти 2 (я перефразирую), он не выполняет некоторые оптимизации, связанные с лексической областью действия и сохранением расположения идентификаторов, потому что эти ключевые слова могут потенциально изменить лексическую область действия, что делает оптимизации компилятора своего рода неправильными (Я предполагаю, что оптимизации - это что-то вроде компилятора, хранящего местоположение каждого идентификатора, чтобы он мог предоставить значение идентификатора, не ища его, когда он запрашивается во время выполнения).
Теперь я понимаю, почему это произойдет, когда вы использовали eval()
Ключевое слово: ваш eval может оценивать пользовательский ввод, и этот пользовательский ввод может быть объявлением новой переменной, которая скрывает другую переменную, к которой вы обращаетесь позже, скажем, функцию, которая выполняется, если компилятор сохранил статическое местоположение, доступ вернул бы значение неправильного идентификатора (так как доступ должен был вернуть значение идентификатора, который был объявлен eval()
, но он вернул значение переменной, которая была сохранена компилятором для оптимизации поиска). Так что я просто предполагаю, что именно поэтому компилятор не выполняет поиск, связанный с областью видимости, всякий раз, когда он обнаруживает eval()
в вашем коде.
Но почему компилятор делает то же самое для with
ключевое слово? Книга говорит, что это так, потому что with
создает новую лексическую область во время выполнения и использует свойства объекта, передаваемого в качестве аргумента with
объявить некоторые новые идентификаторы. Я буквально понятия не имею, что это значит, и мне очень трудно все это визуализировать, так как все, что связано с компилятором, в этой книге - теория.
Я знаю, что могу ошибиться, в таком случае, пожалуйста, исправьте все мои недоразумения:)
2 ответа
Упомянутая здесь оптимизация основана на этом факте: переменные, объявленные внутри функции, всегда можно определить с помощью простого статического анализа кода (т. Е. Путем просмотра 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
существует в baz
или нет, потому что это может быть условно создано содержимым egg
на конкретном пробеге bar
это создало эту внутреннюю функцию. Поэтому его нужно проверять, а оптимизация не используется.
Возьмите этот пример:
{
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 ??*/);
}
}