Изучение области видимости в JavaScript

Подъем - это механизм JavaScript, в котором объявления переменных с использованием var поднимаются / поднимаются в верхнюю часть своей области видимости (в верхнюю часть своей функциональной / локальной области, если они объявлены внутри function или в верхнюю часть своей глобальной области видимости, если они объявлены вне функции), как только JavaScript скомпилирует весь ваш код.

Важно отметить, что подъем перемещает только объявление. Задания остаются на месте.

Давайте рассмотрим пример, чтобы продемонстрировать влияние подъема.

console.log(hoist);
var hoist = 'This variable has been hoisted.';

Как вы думаете, какой будет результат?

  1. Uncaught ReferenceError: hoist is not defined
  2. This variable has been hoisted.
  3. undefined

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

Приведенный выше код такой же, как:

var hoist;
console.log(hoist); // undefined
hoist = 'This variable has been hoisted';

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

function hoist() {
  console.log(message);
  var message = 'Hoisting is done.';
}
hoist();

Если вы угадали undefined, вы правы.

Вот как интерпретатор видит приведенный выше код:

function hoist() {
  var message;
  console.log(message); // undefined
  message = 'Hoisting is done';
}
hoist(); // undefined

Объявление переменной var message, областью действия которой является функция hoist(), поднимается в верхнюю часть функции.

Правильный способ выглядит следующим образом:

function hoist() {
  var message = 'This avoids the above pitfall.';
  console.log(message);
}
hoist(); // This avoids the above pitfall.

Подъемные функции

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

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

hoisted(); // This function has been hoisted.
function hoisted() {
  console.log('This function has been hoisted');
}

Однако функциональные выражения не поднимаются.

expression(); // TypeError: expression is not a function.
var expression = function() {
  console.log('This function has been hoisted');
}

Возьмем другой пример:

function foo() {
  function bar() {
    return 'bar1';
  }
  return bar();
  function bar() {
    return 'bar2';
  }
}
foo();

Угадайте результат?

Результат - bar2. Будет поднята первая функция внутри foo. Точно так же второе объявление функции также поднимется наверх. Поскольку обе функции имеют одинаковое имя, второе объявление функции заменит первое. Следовательно, при выполнении будет вызываться bar, вторая функция.

Но как насчет let и const?

Они также поднимаются - фактически поднимаются объявления var, let, const, function и class.

позволять

Переменные, объявленные с ключевым словом let, являются блочными, а не функциональными. Это просто означает, что область видимости переменной привязана к блоку, в котором она объявлена, а не к функции, в которой она объявлена.

console.log(hoist); // ReferenceError: hoist is not defined
let hoist = 'This variable has been hoisted';

Подобно ключевому слову var, мы ожидаем, что вывод журнала будет undefined.

Экземпляры var и let могут быть инициализированы без значения (и если вы попытаетесь вызвать его, он вернет undefined).

let hoist;
console.log(hoist); // undefined
hoist = 'This variable has been hoisted.';

Const

Ключевое слово const было введено в es6, чтобы разрешить неизменяемые переменные. В const, как и в let, переменная поднимается в верхнюю часть блока.

console.log(hoist); // ReferenceError: hoist is not defined
const hoist = 'This variable has been hoisted';

Но в отличие от let и var, мы не можем инициализировать const без значения. Если вы попытаетесь это сделать, он выдаст Error.

const hoist;
console.log(hoist); 
// SyntaxError: Missing initializer in the const declaration;
hoist = 'This variable has been hoisted';

Следовательно, const и let должны быть объявлены и инициализированы перед использованием.

const foo = 'Hey, I'm foo!';
console.log(foo); // Hey, I'm foo!
let bar = 'Hey, I'm bar!';
console.log(bar); // Hey, I'm bar!

Подъемные классы

Объявление класса

Также поднимаются объявления классов JavaScript. Однако они остаются неинициализированными до оценки.

var foo = new bar();
foo.height = 100;
foo.weight = 100;
console.log(foo); // Guess the output?
class bar {
  constructor(height, weight) {
    this.height = height;
    this.weight = weight;
  }
}

Если вы угадали undefined, то на этот раз ошиблись. Результат - ReferenceError: foo is not defined. Почему? bar использовался перед объявлением, что недопустимо для переменных класса.

class bar {
  constructor(height, weight) {
    this.height = height;
    this.weight = weight;
  }
}
var foo = new bar();
foo.height = 100;
foo.weight = 100;
console.log(foo); // { height: 100, weight: 100 }

Выражения класса

Выражения класса не поднимаются.

var Square = new Polygon();
Square.height = 10;
Square.width = 10;
console.log(Square); // TypeError: Polygon is not a constructor

var Polygon = class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

Правильный способ сделать это:

var Polygon = class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

var Square = new Polygon();
Square.height = 10;
Square.width = 10;
console.log(Square);

Заключение

Мы должны сделать привычкой объявлять и инициализировать переменные, классы и функции JavaScript перед использованием.

Ссылка: https://scotch.io/tutorials/understanding-hoisting-in-javascript