Каков результат следующей программы?
Скопируйте код кода следующим образом:
вар фу = 1;
функциональная панель() {
если (!foo) {
вар фу = 10;
}
оповещение (фу);
}
бар();
Результат — 10;
А что насчет этого?
Скопируйте код кода следующим образом:
вар а = 1;
функция б() {
а = 10;
возвращаться;
функция а() {}
}
б();
предупреждение(а);
Результат 1.
Вас это пугает? Что случилось? Это может показаться странным, опасным и запутанным, но на самом деле это очень полезная и впечатляющая функция языка JavaScript. Я не знаю, есть ли стандартное название для этого поведения, но мне нравится этот термин: «Подъем». В этой статье будет дано вводное объяснение этого механизма, но сначала давайте получим необходимое понимание области применения JavaScript.
Область применения Javascript
Для новичков в Javascript одной из самых запутанных областей является область применения; на самом деле это касается не только новичков; Я встречал некоторых опытных программистов JavaScript, но они не очень хорошо разбираются в области применения. Причина, по которой область JavaScript сбивает с толку, заключается в том, что синтаксис программы сам по себе похож на язык семейства C, как в следующей программе на C:
Скопируйте код кода следующим образом:
#include <stdio.h>
интервал основной() {
интервал х = 1;
printf("%d, ", х); // 1
если (1) {
интервал х = 2;
printf("%d, ", х); // 2);
}
printf("%d/n", x); // 1
}
Выходной результат — 1 2 1. Это связано с тем, что языки семейства C имеют область действия блока. Когда управление программой входит в блок, например блок if, переменные, которые влияют только на блок, могут быть объявлены, не затрагивая эффекты вне блока. домен. Но в Javascript это не работает. Взгляните на код ниже:
Скопируйте код кода следующим образом:
вар х = 1;
консоль.журнал(х); // 1
если (истина) {
вар х = 2;
консоль.журнал(х); // 2
}
консоль.журнал(х); // 2
Результат будет 1 2 2. Потому что javascript — это область действия функции. Это самое большое отличие от языков семейства C. Оператор if в этой программе не создает новую область.
Для многих программистов C, C++ и Java это не то, чего они ожидают и приветствуют. К счастью, благодаря гибкости функций JavaScript, есть способы обойти эту проблему. Если вам необходимо создать временную область, сделайте что-то вроде этого:
Скопируйте код кода следующим образом:
функция Фу() {
вар х = 1;
если (х) {
(функция () {
вар х = 2;
// какой-то другой код
}());
}
// x по-прежнему равен 1.
}
Этот метод является гибким и может использоваться везде, где вы хотите создать временную область. Не только внутри квартала. Однако я настоятельно рекомендую вам потратить время на понимание области действия JavaScript. Это очень полезно и одна из моих любимых функций JavaScript. Если вы понимаете область действия, то подъем переменных будет иметь для вас больше смысла.
Объявление переменной, именование и продвижение
В JavaScript существует 4 основных способа входа переменных в область видимости:
•1 Встроенный язык: все области имеют this и аргументы (Примечание переводчика: после тестирования аргументы не отображаются в глобальной области видимости);
•2 Формальные параметры: формальные параметры функции будут частью области действия тела функции;
•3 Объявление функции: вот такая форма: function foo(){};
•4 Объявление переменной: например: var foo;
Объявления функций и объявления переменных всегда незаметно «поднимаются» интерпретатором в начало тела метода. Это означает, что код должен выглядеть следующим образом:
Скопируйте код кода следующим образом:
функция Фу() {
бар();
вар х = 1;
}
на самом деле будет интерпретироваться как:
Скопируйте код кода следующим образом:
функция Фу() {
вар х;
бар();
х = 1;
}
Независимо от того, может ли быть выполнен блок, в котором определена переменная. Следующие две функции на самом деле одно и то же:
Скопируйте код кода следующим образом:
функция Фу() {
если (ложь) {
вар х = 1;
}
возвращаться;
вар у = 1;
}
функция Фу() {
вар х, у;
если (ложь) {
х = 1;
}
возвращаться;
у = 1;
}
Обратите внимание, что назначения переменных не поднимаются, а только объявляются. Однако объявление функции немного отличается, и тело функции также продвигается. Но обратите внимание, что существует два способа объявления функции:
Скопируйте код кода следующим образом:
функция тест() {
foo(); // Ошибка типа «foo не является функцией»
bar(); // "это заработает!"
var foo = function () { // переменная указывает на выражение функции
alert("Это не запустится!");
}
function bar() { // Функция объявления функции с именем bar
alert("Это будет работать!");
}
}
тест();
В этом примере вместе с телом функции поднимаются только функциональные объявления. Объявление foo будет поднято, но тело функции, на которое оно указывает, будет назначено только во время выполнения.
Вышеизложенное охватывает некоторые основы буста, и они не кажутся такими уж запутанными. Однако в некоторых особых сценариях все же существует определенная степень сложности.
Порядок разбора переменной
Самое важное, о чем следует помнить, — это порядок изменения разрешения. Помните 4 способа, с помощью которых именование входит в область действия, о которой я говорил ранее? Порядок анализа переменных соответствует тому, в котором я их перечислил.
Скопируйте код кода следующим образом:
<скрипт>
функция а(){
}
вар а;
alert(a);//Распечатываем тело функции
</скрипт>
<скрипт>
вар а;
функция а(){
}
alert(a);//Распечатываем тело функции
</скрипт>
//Но обратите внимание на разницу между следующими двумя методами записи:
<скрипт>
вар а=1;
функция а(){
}
alert(a);//Распечатываем 1
</скрипт>
<скрипт>
функция а(){
}
вар а=1;
alert(a);//Распечатываем 1
</скрипт>
Здесь есть 3 исключения:
1 Встроенные аргументы имени ведут себя странно. Кажется, его следует объявлять после формальных параметров функции, но до объявления функции. Это значит, что если в формальном параметре есть аргументы, то он будет иметь приоритет над встроенным. Это очень плохая функция, поэтому избегайте использования аргументов в формальных параметрах;
2 Определение этой переменной в любом месте приведет к синтаксической ошибке, что является хорошей особенностью;
3. Если несколько формальных параметров имеют одно и то же имя, приоритет имеет последний, даже если его значение не определено во время реальной работы;
именованная функция
Вы можете дать функции имя. Если да, то это не объявление функции, и указанное имя функции (если оно есть, например, спам ниже, примечание переводчика) в определении тела функции не будет повышено, а проигнорировано. Вот код, который поможет вам понять:
Скопируйте код кода следующим образом:
foo(); // Ошибка типа «foo не является функцией»
бар(); // допустимо
baz(); // Ошибка типа «baz не является функцией»
spam(); // Ошибка ссылки «спам не определен»
var foo = function () {} // foo указывает на анонимную функцию
функция bar() {} // объявление функции
var baz = function spam() {} // Именованная функция, продвигается только baz, спам продвигаться не будет.
Фу(); // допустимо
бар(); // допустимо
баз(); // допустимо
spam(); // Ошибка ссылки «спам не определен»
Как писать код
Теперь, когда вы понимаете область видимости и подъем переменных, что это значит для кодирования на JavaScript? Самое главное — всегда определять переменные с помощью var. И я настоятельно рекомендую, чтобы для имени всегда было только одно объявление var в области видимости. Если вы сделаете это, у вас не возникнет проблем с областью видимости и подъемом переменных.
Что вы подразумеваете под спецификацией языка?
Я считаю, что справочная документация ECMAScript всегда полезна. Вот что я нашел об области видимости и подъеме переменных:
Если переменная объявлена в классе тела функции, это область действия функции. В противном случае он имеет глобальную область действия (как свойство global). Переменные будут созданы, когда выполнение войдет в область видимости. Блоки не будут определять новые области видимости, только объявления функций и процедуры (транслятор думает, что это глобальное выполнение кода) будут создавать новые области видимости. Переменные инициализируются как неопределенные при их создании. Если в операторе объявления переменной есть операция присваивания, операция присваивания произойдет только при ее выполнении, а не при ее создании.
Я надеюсь, что эта статья прольет свет программистам, которые не понимают JavaScript. Я также изо всех сил стараюсь не вызывать еще большей путаницы. Если я сказал что-то не так или что-то упустил, пожалуйста, дайте мне знать.
Приложение переводчика
Друг напомнил мне о проблеме продвижения именованных функций в глобальной области видимости в IE:
Вот как я это проверял, когда переводил статью:
Скопируйте код кода следующим образом:
<скрипт>
функция(){
спам();
var baz = function spam() {alert('это спам')};
}
т();
</скрипт>
Этот способ написания, то есть продвижение именованных функций в неглобальную область видимости, имеет одинаковую производительность в ie и ff. Я изменил его на:
Скопируйте код кода следующим образом:
<скрипт>
спам();
var baz = function spam() {alert('это спам')};
</скрипт>
Тогда спам можно запускать под ie, но не под ff. Это показывает, что разные браузеры по-разному обрабатывают эту деталь.
Этот вопрос также заставил меня задуматься над двумя другими вопросами: 1. Для переменных, имеющих глобальную область видимости, существует разница между var и не-var. Без var переменная не будет повышена. Например, из следующих двух программ вторая сообщит об ошибке:
Скопируйте код кода следующим образом:
<скрипт>
предупреждение(а);
вар а=1;
</скрипт>
Скопируйте код кода следующим образом:
<скрипт>
предупреждение(а);
а=1;
</скрипт>
2: Локальные переменные, созданные в eval, не будут повышены (у него нет возможности сделать это).
Скопируйте код кода следующим образом:
<скрипт>
вар а = 1;
функция т(){
предупреждение(а);
eval('var a = 2');
предупреждение(а);
}
т();
предупреждение(а);
</скрипт>