Хотя знания, связанные с областью действия, являются основой JavaScript, полное понимание должно начинаться с принципа.Лексическая/динамическая область видимости,размах (цепочка),Переменная/функция подъема,Закрытие,вывоз мусора
Принцип составления
Для большинства языков программирования компиляция состоит примерно из трех шагов.
-
Слово/лексический анализ (Tokenizing/Lexing)
Этот процесс разбивает исходный код на
词法单元 (token), как кодconst firstName = 'Yancey'будет разбит наconst,firstName,=,'Yancey', Будут ли пробелы трактоваться как лексические единицы, в зависимости от значения пробелов на данном языке. Здесь рекомендуют сайтParserМожет использоваться для анализа исходного кода JavaScript.Для этого примера структура сегментации слов выглядит следующим образом.[ { type: 'Keyword', value: 'const', }, { type: 'Identifier', value: 'firstName', }, { type: 'Punctuator', value: '=', }, { type: 'String', value: "'Yancey'", }, ]; -
Парсинг/Парсинг (Парсинг)
Этот процесс преобразует поток токенов вабстрактное синтаксическое дерево(Абстрактное синтаксическое дерево, AST). Синтаксический анализ будет преобразован в AST в соответствии со стандартом ECMAScript. Например, если вы напишете
const new = 'Yancey', сообщит об ошибкеUncaught SyntaxError: Unexpected token new.Для приведенного выше примера сгенерированный AST показан на следующем рисунке, где
Identifierпредставляет имя переменной,Literalпредставляет значение переменной. -
генерация кода
На этом этапе AST преобразуется в исполняемый код, подобно тому, как движок V8 компилирует строки JavaScript в двоичный код (создание переменных, выделение памяти, сохранение значения в переменной...)
В дополнение к вышеупомянутым трем этапам, механизм JavaScript такжеРазбор,генерация кода,процесс компиляцииAcorn
Есть две модели охвата, однаЛексическая область, другойДинамическая область.
Лексическая область — это область, определенная на лексической фазе, другими словами, когда вы пишете переменную и блочную область при написании кода.evalа такжеwithдля изменения лексической области видимости, но в обоих случаях движок не оптимизирует поиск области видимости во время компиляции, поэтому не используйте их.
Динамическая область определяется во время выполнения, наиболее типичная вещь.
объем
Будь то этап компиляции во время выполнения, неразделимыдвигатель, переводчик, объем.
-
Движок отвечает за компиляцию и выполнение программ JavaScript.
-
Компилятор отвечает за синтаксический анализ, генерацию кода и т.д.
-
Области используются для сбора и поддержки всех правил доступа к переменным.
в кодеconst firstName = 'Yancey'Например, сначала компилятор встречаетconst firstName, спроситобъемУже есть переменная с тем же именем в текущем наборе области области, компилятор игнорирует декларацию, если есть один, в противном случае он объявляет новую переменную в текущем наборе области с именем какfirstName.
Затем компилятор будет генерировать код, необходимый для запуска двигателя для обработкиfirstName = 'Yancey'Эта операция присваивания.Движок сначала спросит у области, есть ли переменная в текущем наборе областей, называемыхfirstName, Если есть, движок будет использовать эту переменную, иначе продолжит поиск.
Движок ищет элементы в области видимости двумя способами:LHSа такжеRHS. как правило,LHSпредставляет собой поиск на этапе назначения, иRHSЭто просто найти переменную.
См. пример ниже.
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
-
var c = foo(2);Двигатель будет смотреть в область, чтобы увидеть, есть лиfooЭта функция, которая должна найти RHS, найденную после того, как она присваивается переменнойc, это поиск LHS. -
function foo(a) {Вот и будут реальные параметры2присвоить параметруa, так что это поиск LHS. -
var b = a;Здесь нам нужно сначала найти переменнуюa, так что это поиск по правой стороне. Тогда переменнаяaназначить наb, это поиск LHS. -
return a + b;найтиaа такжеb
глобальная область
-
самая внешняя функцияа такжевне самой внешней функцииОпределенные переменные имеют глобальную область видимости
-
Все переменные, которые не определены и назначены напрямую, автоматически объявляются глобальными.
-
Все свойства объекта окна имеют глобальную область видимости.
const a = 1; // 全局变量
// 全局函数
function foo() {
b = 2; // 未定义却赋初值被认为是全局变量
const name = 'yancey'; // 局部变量
// 局部函数
function bar() {
console.log(name);
}
}
window.navigator; // window 对象的属性拥有全局作用域
Недостаток глобальной области видимости очевиден, то есть она будет загрязнять глобальное пространство имен, поэтому исходный код многих библиотек будет использовать(function(){....})()Кроме того, широкое использование модульности (ES6, commonjs и т. д.) также обеспечивает лучшее решение для предотвращения загрязнения глобального пространства имен.
объем функции
Сфера действия функций означает, что все переменные, принадлежащие этой функции, могут использоваться и повторно использовать во всей области функции.
function foo() {
const name = 'Yancey';
function sayName() {
console.log(`Hello, ${name}`);
}
sayName();
}
foo(); // 'Hello, Yancey'
console.log(name); // 外部无法访问到内部变量
sayName(); // 外部无法访问到内部函数
Примечательно, что если, переключаться, в то время как для этих условных утверждений или цикла не создает нового объема, хотя он также имеет пару{}Обертывание Доступ к внутренним переменным зависит от того, как они объявлены (var или let/const).
if (true) {
var name = 'yancey';
const age = 18;
}
console.log(name); // 'yancey'
console.log(age); // 报错
область действия блока
Мы знаем, что появление let и const изменило ситуацию, когда JavaScript не имеет блочной области видимости (подробности см. на стр. 76 уровня 3, в то время не было понятия блочной области видимости). подробности о let и const, это Если вы не понимаете два... но я представлю это позжеВременная мертвая зонаКонцепция чего-либо.
также,try/catchизcatchПредложение также создает область блока, см. следующий пример:
try {
noThisFunction(); // 创造一个异常
} catch (e) {
console.log(e); // 可以捕获到异常
}
console.log(e); // 报错, 外部无法拿到 e
продвигать
В "дикую эпоху" до ES6 переменное продвижение часто задавали в интервью, а появление let и const решило проблему переменного продвижения.Но функция продвижения существовала всегда, здесь мы исходим из принципа анализировать продвижение.
переменное продвижение
Поэтому для кодаvar i = 2;, JavaScript фактически увидит этот код какvar i;а такжеi = 2Первый — ждать на этапе компиляции, второй — ждать этапа выполнения, Другими словами, этот процесс объявит объявленную переменную и функцию в верхней части своей области, что называется улучшением.
Вам может быть интересно, почему для let и const нет продвижения переменных? Это связано с тем, что на этапе компиляции, когда встречается объявление переменной, компилятор либо поднимает его наверх области видимости (объявление var), либо помещает вВременная мертвая зона, TDZ), то есть переменные, объявленные с помощью let или const.Доступ к переменным в TDZ вызывает ошибку времени выполнения, только после того, как оператор объявления переменной будет выполнен, переменная будет удалена из TDZ и может быть доступна в это время.
Сможете ли вы правильно ответить на все следующие примеры?
typeof null; // 'object'
typeof []; // 'object'
typeof someStr; // 'undefined'
typeof str; // Uncaught ReferenceError: str is not defined
const str = 'Yancey';
первый, потому чтоnullв основном указатель, поэтому вернется'object'На более глубоком уровне различные объекты представлены как двоичные на нижнем уровне.В Javascript первые три цифры двоичного кода равны 0, что будет рассматриваться как тип объекта, двоичный код нулевого значения равен 0, а первые три естественно равны 0, поэтому функция execute typeof вернется, когда'object'.
Второе, что я хочу подчеркнуть, это то, что когда typeof оценивает переменную ссылочного типа, все, что он получает, это'object', поэтому оператор не может правильно различать конкретные типы, такие как Array или RegExp.
Третье, когда тип aне объявленПеременные, не жалуются, но возвращает «неопределенные»
четвертый,str
функция подъема
Поднимаются как объявления функций, так и объявления переменных, но стоит отметить, что сначала поднимаются функции, а затем переменные.
test();
function test() {
foo();
bar();
var foo = function() {
console.log("this won't run!");
};
function bar() {
console.log('this will run!');
}
}
Приведенный выше код примет следующий вид: ВнутреннийbarФункция поднимается наверх, чтобы ее можно было выполнить; тогда переменнаяfooбудет поднят наверх, но переменная не может быть выполнена, поэтому выполнитеfoo()сообщит об ошибке.
function test() {
var foo;
function bar() {
console.log('this will run!');
}
foo();
bar();
foo = function() {
console.log("this won't run!");
};
}
test();
Закрытие
Закрытия - это функции, которые имеют доступ к независимым (свободным) переменным (переменные, используемые локально, но определенные в прилагаемом объеме). Другими словами, эти функции могут «запомнить» среду, в которой они были созданы. - MDN
Замыкание — это функция, которая имеет доступ к области видимости другой функции — Расширенное программирование на JavaScript (3-е издание).
Объекты-функции могут быть связаны друг с другом через цепочку областей видимости, а переменные внутри тела функции могут храниться в области видимости функции. В литературе по информатике эта функция называется замыканием. — «Полное руководство по JavaScript (6-е издание)». "
Закрытие происходит, когда функция может запомнить и получить доступ к лексической области, в которой она находится, даже если функция выполняется за пределами текущей лексической области видимости — «JavaScript, которого вы не знаете (том 1)».
Кажется, что последнее объяснение легче понять, поэтому давайте изучим замыкания из «запомнить и получить доступ».
Что такое «помнить»
В JavaScript, если функция вызывается и не будет использоваться в дальнейшем, механизм сборки мусора (описан ниже) уничтожит созданную функцией область видимости.Мы знаем, что переменная ссылочного типа — это просто указатель, не копирует реальное значение переменной, но копируетМесто нахожденияпередается переменной, поэтому, когда функция передается переменной в области видимости, которая еще не уничтожена, функция существует, потому что переменная существует, а существование функции зависит от лексической области видимости, в которой функция находится, лексическая область действия функции is также будет существовать, так что лексическая область видимости «запоминается».
Взгляните на пример ниже.appleфункция,outputСсылка на передается в качестве параметраfruitфункциональныйarg, Таким образом, вfruitВо время выполнения функцииargсуществует, так чтоoutputтакже существует иoutputзависимыйappleЛокальная область видимости, созданная функцией, также существует.outputФункция "запоминает"appleПричина области действия.
function apple() {
var count = 0;
function output() {
console.log(count);
}
fruit(output);
}
function fruit(arg) {
console.log('fruit');
}
apple(); // fruit
«Запомнить» и «доступ»
Но приведенный выше пример не является полным «закрытием», потому что он только «запоминает» объем, но не «посещает» область. Давайте модифицируем приведенный выше пример, вfruitвыполнять в функцииargoutputappleв функцииcountПеременная.
function apple() {
var count = 0;
function output() {
console.log(count);
}
fruit(output);
}
function fruit(arg) {
arg(); // 这就是闭包!
}
apple(); // 0
for (var i = 0; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Поскольку setTimeout выполняется асинхронно, задача добавляется в очередь задач через 1000 миллисекунд, а задачи в очереди задач будут выполняться только после выполнения всех задач в основном потоке, поэтому, когда цикл for основного потока выполняется, значение i равно 5, и используйте это время для выполнения задач в очереди задач, поэтому i равно всем 5. И поскольку оно используется в цикле forvarобъявленiнаходится в глобальной области видимости, поэтомуtimerФункция распечатанаiКонечно все 5.
Мы можем генерировать новую область для каждой итерации, используя IIFE внутри итерации, так что обратный вызов отложенной функции может включать новую область внутри каждой итерации, и каждая итерация будет содержать новую область с правильными значениями переменных для нас. доступ. Код показан ниже.
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
Если внимательно посмотреть на API, то его тоже можно записать в следующем виде:
for (var i = 0; i < 5; i++) {
setTimeout(function(j) {
console.log(j);
}, i * 1000, i);
}
Конечно, лучший способ состоит в том, чтобы использовать точку объявления I, на этот раз переменная я могу действовать на этом блоке петли, каждая итерация использует значение, завершаемое предыдущую итерацию для инициализации этой переменной.
for (let i = 0; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
вывоз мусора
пометить как очищенныйа такжеподсчет ссылокпометить как очищенный.
пометить как очищенный
Сборщик мусора пометит все переменные, хранящиеся в памяти, при запуске, а затем снимет пометку с переменных в среде и переменных, на которые ссылаются переменные в среде.После этого переменные, помеченные как переменные, считаются готовыми к удалению, потому что переменные в среде больше не доступны для них Наконец, сборщик мусора завершает очистку памяти, уничтожая помеченные значения и освобождая место в памяти, которое они занимали.
подсчет ссылок
Количество ссылок - это количество раз, когда отслеживание каждого значения ссылается. Когда переменная объявлена, эта ссылка стоит 1; число 1; противоположное другое значение, это стоит ссылки на количество ссылок; память Занятые по количеству ценностей 0 выпущены в следующий раз. Недостатки: Недостатки: Недостатки: Недостатки: Недостатки: Недостаткициклическая ссылкаЭто приведет к цитируемости никогда не 0.
Суммировать
В: Что такое масштаб?
A: Scope — это набор правил поиска переменных по имени.
Вопрос. Что такое цепочка областей действия?
A: Вложенность области видимости происходит, когда блок или функция вложена в другой блок или другую функцию, поэтому, когда переменная не может быть найдена в текущей области видимости, она переходит во внешнюю вложенную область видимости Область продолжает поиск до тех пор, пока переменная не будет найдена или достигнута глобальная область, и сообщается об ошибке, если она не найдена в глобальной области.Этот шаблон поэтапного поиска представляет собой цепочку областей.
Q: Что такое закрытие?
A: Когда функция может вспомнить, где и когда получить доступ к лексической области видимости, она создает замыкание, даже если функция выполняется за пределами текущей лексической области видимости.
наконец
опросчертvar
Закончите письменным тестовым вопросом: напишите функцию, первый вызов возвращает 0, а каждый последующий вызов возвращает на 1 больше, чем предыдущий.Этот вопрос не сложный, в основном при изучении замыканий и функций немедленного выполнения.Ответ, который я написал, такой следует. Если у вас есть лучшее решение, поделитесь им в комментариях.
const add = (() => {
let num = 0;
return () => num++;
})();
Ссылаться на
Расширенное программирование с помощью JavaScript (третье издание) - Николас С. Закас
«Понимание ES6» — Николас С. Закас
JavaScript Вы не знаете (том 1) - Кайл Симпсон
лексическая область видимости в javascript
Объем и замыкания в «JavaScript Breakthrough»
Глубокое понимание области видимости JavaScript и цепочки областей видимости
Принцип компиляции JavaScript, компилятор, движок и область применения
Закрытие области, вы действительно понимаете?
Добро пожаловать в мой официальный аккаунт: начало атаки