Современная переменная область JavaScript

Node.js внешний интерфейс Babel

Оригинальная ссылка:Variable Scope in Modern JavaScript

Переводчик:OFED

Область видимости переменных в современном JavaScript

Когда я разговариваю с другими разработчиками JavaScript, я часто удивляюсь, как много людей не знаютпеременная область видимостиКак это работает в JavaScript. Под областью действия мы подразумеваем видимость переменных в коде или, другими словами, какие части кода могут получать доступ к переменным и изменять их. Я обнаружил, что люди часто используют в кодеvarОбъявляйте переменные, не зная, что с ними сделает JavaScript.

JavaScript претерпел некоторые существенные изменения за последние несколько лет; эти изменения включают новые ключевые слова объявления переменных и новые способы обработки областей видимости. Добавлен ES6 (ES2015)letа такжеconstКоманда, прошло уже три года, и браузер очень хорошо ее поддерживает. Для других новых функций вы можете использоватьBabelПереведите ES6 на широко поддерживаемый JavaScript. Пришло время рассмотреть, как объявляются переменные, и узнать больше об области видимости.

В этом сообщении блога я покажу вам несколько примеров JavaScript.Глобальный,местныйа такжеблочный уровеньКак работают прицелы. Мы также продемонстрируем, как использовать для тех, кто еще не знаком с этой частьюletа такжеconstобъявить переменные.

глобальная область

Давайте начнем сглобальная областьГоворя о. К глобально определенным переменным можно получить доступ и изменить их в любом месте кода (почти, но об исключениях мы поговорим позже).

Переменная, объявленная в области верхнего уровня вне какой-либо функции, является глобальной переменной.

var a = 'Fred Flinstone'; // 全局变量
function alpha() {
    console.log(a);
}
alpha(); // 输出 'Fred Flinstone'

В этом примереaявляется глобальной переменной, поэтому легко доступна в любой функции. Таким образом, мы можем начать с методаalphaвыходaценность . когда мы звонимalphaметод, вывод консолиFred Flinstone.

Когда веб-браузер объявляет глобальную переменную, он действует как глобальная переменная.windowсвойства объекта. Посмотрите этот пример:

var b = 'Wilma Flintstone';
window.b = 'Betty Rubble';
console.log(b); // 输出 'Betty Rubble'

bможно использовать какwindowсвойства объекта (window.b) доступ/изменение. Конечно, не обязательно проходитьwindowмодификация объектаbзначение, это только для того, чтобы доказать это. Мы, скорее всего, запишем приведенный выше случай как:

var b = 'Wilma Flintstone';
b = 'Betty Rubble';
console.log(b); // 输出 'Betty Rubble'

Будьте осторожны с глобальными переменными. Они делают код менее читаемым и трудным для тестирования. Я видел, как у многих разработчиков возникают неожиданные проблемы с обнаружением сброса значения переменной. Передача переменных в качестве аргументов функциям намного лучше, чем использование глобальных переменных. Глобальные переменные следует использовать с осторожностью.

Если вам нужно использовать глобальные переменные, лучше определить пространства имен, чтобы они были свойствами глобального объекта. Например, создайтеglobalsилиappглобальный объект.

var app = {}; // 全局对象
app.foo = 'Homer';
app.bar = 'Marge';
function beta() {
    console.log(app.bar);
}
beta(); // 输出 'Marge'

Если вы используете NodeJS, область верхнего уровня не совпадает с глобальной областью. Если используется в модуле NodeJSvar foobar, это локальная переменная этого модуля. Чтобы определить глобальные переменные в NodeJS, нам нужно использоватьобъект глобального пространства имен,global.

global.foobar = 'Hello World!'; // 在 NodeJS 里是一个全局变量

Обратите внимание, что если ключевое слово не используетсяvar,letилиconstОдин для объявления переменной, затем переменная принадлежит глобальной области видимости.

function gamma() {
    c = 'Top Cat';
}
gamma();
console.log(c); // 输出 'Top Cat'
console.log(window.c); // 输出 'Top Cat'

Мы рекомендуем всегда использовать ключевое слово variable для определения переменных. Таким образом, можно контролировать область действия каждой переменной в коде. Как и в приведенных выше примерах, надеюсь, вы знаете о потенциальных опасностях неиспользования ключевых слов.

локальная область

теперь мы возвращаемся клокальная область

var a = 'Daffy Duck'; // a 是全局变量
function delta(b) {
  // b 是传入 delta 的局部变量
  console.log(b);
}
function epsilon() {
  // c 被定义成局部作用域变量
  var c = 'Bugs Bunny';
  console.log(c);
}
delta(a); // 输出 'Daffy Duck'
epsilon(); // 输出 'Bugs Bunny'
console.log(b); // 抛出错误:b 在全局作用域未定义

Переменные, определенные внутри функции, находятся внутри функции. В приведенном выше примере b и c являются локальными для соответствующих функций. Но каков будет результат вывода, когда произойдет следующий способ записи?

var d = 'Tom';
function zeta() {
  if (d === undefined) {
    var d = 'Jerry';
  }
  console.log(d);
}
zeta();

Ответ «Джерри», вероятно, один из часто задаваемых вопросов на интервью. Внутри дзета-функции определена новая локальная переменная d.varПри определении переменной JavaScript инициализирует ее в верхней части текущей области видимости, независимо от того, где она находится в коде.

var d = 'Tom';
function zeta() {
  var d;
  if (d === undefined) {
    d = 'Jerry';
  }
  console.log(d);
}
zeta();

это называетсяпродвигать, что является одной из особенностей JavaScript, и следует отметить, что переменные не инициализируются в верхней части области видимости, что может легко привести к некоторым ошибкам. отличноletа такжеconstпоявился, чтобы спасти нас. Итак, давайте посмотрим, как использоватьletСоздать область блока.

область действия блока

С появлением ES6 несколько лет назад появились два новых ключевых слова для объявления переменных:letа такжеconst. Оба ключевых слова позволяют расширить объем блока кода, то есть контент, введенный между двумя бреками {}.

let

многие люди думаютletк существующемуvarзаменять. Однако это не совсем так, потому что они объявляют переменные в разных областях.letобъявляет блочную переменную, однакоvarОператоры позволяют нам создавать переменные локального масштаба. Конечно, внутри функции мы можем использоватьletобъявить область блока, как мы привыклиvarТакой же.

function eta() {
    let a = 'Scooby Doo';
}
eta();

здесьaОбласть действия функцииetaВнутри. Мы также можем использовать условные блоки и циклы. Область действия на уровне блока включает любые подблоки, содержащиеся в блоке верхнего уровня определения переменной.

for (let b = 0; b < 5; b++) {
    if (b % 2) {
        console.log(b);
    }
}
console.log(b); // 'ReferenceError: b is not defined'

В этом примереbсуществуетforРаботает на уровне блоков (включая условные блоки) в рамках цикла. Таким образом, он выведет нечетные числа 1 и 3, а затем выдаст ошибку, потому что мы не можем получить к нему доступ за пределами его области видимости.b.

Мы уже видели странный подъем переменных в JavaScript, влияющий на функции.zetaВ результате, что произойдет, если мы перепишем функцию, чтобы использовать let?

var d = 'Tom';
function zeta() {
    if (d === undefined) {
        let d = 'Jerry';
    }
    console.log(d);
}
zeta();

этот разzetaвыводит «Том», потому что d имеет право действовать внутри условного блока, но означает ли это, что здесь нет подъема? Нет, когда мы используемletилиconst, JavaScript по-прежнему будет поднимать переменную в верхнюю часть области видимости, но сvarразница в том,varПосле начального значения обновления переменной декларацииundefined,letа такжеconstОбъявленные переменные после подъема не инициализируются, они находятся во временной мертвой зоне.

Давайте посмотрим, что происходит, когда вы используете блочную переменную перед объявлением инициализации.

function theta() {
    console.log(e); // 输出 'undefined'
    console.log(f); // 'ReferenceError: d is not defined'
    var e = 'Wile E. Coyote';
    let f = 'Road Runner';
}
theta();

Поэтому вызовthetaпеременные, которые будут иметь локальную область видимостиeвыходundefined, а для переменных с блочной областью видимостиfвыдает ошибку. при запускеfРаньше мы не могли его использовать, в данном случае мы установили его значение «Road Runner».

Прежде чем продолжить, нам нужно уточнить, что есть еще одно важное различие между let и var. Когда мы используем var на самом верхнем уровне нашего кода, она становится глобальной переменной и добавляется к объекту окна в браузере. С помощью let, несмотря на то, что переменная станет глобальной, поскольку ее область действия представляет собой блок всей кодовой базы, она не станет свойством объекта окна.

var g = 'Pinky';
let h = 'The Brain';
console.log(window.g); // 输出 'Pinky'
console.log(window.h); // 输出 undefined

const

Я упоминал ранее, кстатиconst. Это ключевое слово связано сletПредставлены вместе как часть ES6. По объему это то же самое, что иletработает так же.

if (true) {
  const a = 'Count Duckula';
  console.log(a); // 输出 'Count Duckula'
}
console.log(a); // 输出 'ReferenceError: a is not defined'

В этом примереaОбласть примененияifоператора, поэтому к нему можно получить доступ внутри условного оператора, но вне условного оператораundefined.

а такжеletразные,constОпределенные переменные нельзя изменить путем переназначения.

const b = 'Danger Mouse';
b = 'Greenback'; // 抛出 'TypeError: Assignment to constant variable'

Однако ситуация немного отличается при работе с массивами или объектами. Мы по-прежнему не можем переназначить, поэтому следующее не удастся

const c = ['Sylvester', 'Tweety'];
c = ['Tom', 'Jerry']; // 抛出 'TypeError: Assignment to constant variable'

Однако мы можем изменять константные массивы или объекты, если только мы не используем для переменныхObject.freeze()сделать его неизменным.

const d = ['Dick Dastardly', 'Muttley'];
d.pop();
d.push('Penelope Pitstop');
Object.freeze(d);
console.log(d); // 输出 ["Dick Dastardly", "Penelope Pitstop"]
d.push('Professor Pat Pending'); // 抛出错误

глобальная + локальная область

Что происходит, когда мы переопределяем существующую глобальную переменную в локальной области видимости.

var a = 'Johnny Bravo'; // 全局作用域
function iota() {
  var a = 'Momma'; // 局部作用域
  console.log(a); // 输出 'Momma'
  console.log(window.a); // 输出 'Johnny Bravo'
}
iota();
console.log(a); // 输出 'Johnny Bravo'

Когда мы переопределяем глобальную переменную в локальной области видимости, JavaScript инициализирует новую локальную переменную. В примере уже есть глобальная переменная a, а внутри функции iota создается новая локальная переменная a. Новая локальная переменная не изменяет глобальную переменную.Если мы хотим получить доступ к значению глобальной переменной внутри функции, нам нужно использовать глобальную переменную.windowобъект.

Для меня следующий код более удобочитаем, используя глобальное пространство имен вместо глобальных переменных, переписывая нашу функцию для использования области видимости на уровне блоков:

var globals = {};
globals.a = 'Johnny Bravo'; // 全局作用域
function iota() {
    let a = 'Momma'; // 局部作用域
    console.log(a); // 输出 'Momma'
    console.log(globals.a); // 输出 'Johnny Bravo'
}
iota();
console.log(globals.a); // 输出 'Johnny Bravo'

локальная + блочная область

Надеюсь, следующий код работает так, как вы хотите.

function kappa() {
    var a = 'Him'; // 局部作用域
    if (true) {
        let a = 'Mojo Jojo'; // 块级作用域
        console.log(a); // 输出 'Mojo Jojo'
    }
    console.log(a); // 输出 'Him'
}
kappa();

Вышеприведенный код не особо читаем, но доступ к переменным в области блока возможен только в пределах определенного уровня блока. Изменение переменных уровня блока вне области действия уровня блока не имеет никакого эффекта, используйтеletПереопределение переменной a также не дает результата, как в следующем примере:

function kappa() {
    let a = 'Him';
    if (true) {
        let a = 'Mojo Jojo';
        console.log(a); // 输出 'Mojo Jojo'
    }
    console.log(a); // 输出 'Him'
}
kappa();

var, let или const?

Я надеюсь, что это краткое изложение области видимости помогло вам лучше понять, как JavaScript обрабатывает переменные. Во всех примерах я использую var, let и const для определения переменных. С появлением ES6 мы можем заменить var на let и const.

Так является ли var избыточным? Нет действительно правильного или неправильного ответа, но лично я все еще использую var для определения глобальных переменных верхнего уровня. Однако я бы консервативно использовал глобальные переменные и вместо этого использовал бы глобальное пространство имен. Также я использую const для переменных, которые больше не меняются, а let для остальных.

Как вы будете определять переменные в конце, или, надеюсь, вы лучше понимаете область видимости в своем коде.