Переменные, объявленные с помощью let или const, не отображаются в ES6?

Я некоторое время играл с ES6 и заметил, что переменные объявлены с var подняты как положено...

console.log(typeof name); // undefined
var name = "John";

... переменные объявлены с let или же const похоже, есть некоторые проблемы с подъемом:

console.log(typeof name); // ReferenceError
let name = "John";

а также

console.log(typeof name); // ReferenceError
const name = "John";

Означает ли это, что переменные объявлены с let или же const не подняты? Что на самом деле здесь происходит? Есть ли разница между let а также const в этом вопросе?

9 ответов

Решение

@thefourtheye правильно говорит, что эти переменные не могут быть доступны до их объявления. Тем не менее, это немного сложнее, чем это.

Переменные объявлены с let или же const не лапы? Что на самом деле здесь происходит?

Все объявления (var, let, const, function, function*, class) "подняты" в JavaScript. Это означает, что если имя объявляется в области, в этой области идентификатор всегда будет ссылаться на эту конкретную переменную:

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

Это верно как для функциональных, так и для блочных областей 1.

Разница между var / function / function* декларации и let / const / class объявления это инициализация.
Первые инициализируются с undefined или (генератор) функция прямо, когда привязка создается в верхней части области. Однако лексически объявленные переменные остаются неинициализированными. Это означает, что ReferenceError исключение выдается при попытке доступа к нему. Инициализируется только тогда, когда let / const / class Оператор оценивается, все до (выше), что называется временной мертвой зоной.

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

Обратите внимание, что let y; оператор инициализирует переменную с undefined лайк let y = undefined; будет иметь.

Временная мертвая зона - это не синтаксическое местоположение, а время между созданием переменной (области действия) и инициализацией. Ссылка на переменную в коде над объявлением не является ошибкой, если этот код не выполняется (например, тело функции или просто мертвый код), и будет выдано исключение, если вы получите доступ к переменной до инициализации, даже если доступ код находится ниже объявления (например, в объявлении поднятой функции, которое вызывается слишком рано).

Есть ли разница между let а также const в этом вопросе?

Нет, они работают так же, как считается подъем. Единственная разница между ними заключается в том, что const Муравей должен быть и может быть назначен только в инициализирующей части объявления (const one = 1;, и то и другое const one; а потом переназначения вроде one = 2 недействительны).

1: var декларации все еще работают только на уровне функций, конечно

Цитирование спецификаций ECMAScript 6 (ECMAScript 2015), let а также const раздел объявлений,

Переменные создаются, когда создается экземпляр их содержащей Lexical Environment, но к ним нельзя получить доступ, пока не будет оценена переменная LexicalBinding.

Итак, чтобы ответить на ваш вопрос, да, let а также const Поднимите, но вы не можете получить к ним доступ до того, как фактическое объявление будет оценено во время выполнения.

ES6 вводит Let переменные, которые приходят с block level scoping, До тех пор ES5 у нас нет block level scopingпоэтому переменные, объявленные внутри блока, всегда hoisted для определения уровня работы.

В принципе Scope указывает, где в вашей программе видны ваши переменные, что определяет, где вам разрешено использовать объявленные вами переменные. В ES5 у нас есть global scope,function scope and try/catch scope, с ES6 мы также получаем определение уровня блока, используя Let.

  • Когда вы определяете переменную с var Ключевое слово, известно всю функцию с момента ее определения.
  • Когда вы определяете переменную с let Заявление это известно только в блоке, он определен.

     function doSomething(arr){
         //i is known here but undefined
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(var i=0; i<arr.length; i++){
             //i is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(let j=0; j<arr.length; j++){
             //j is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
     }
    
     doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);
    

Если вы запустите код, вы можете увидеть переменную j известен только в loop а не до и после. Тем не менее, наша переменная i известен в entire function с момента, когда это определено.

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

for(var i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

for(let i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

Первый for цикл всегда печатает последнее значение, с let это создает новую область и связывает новые ценности, печатая нас 1, 2, 3, 4, 5,

Подходит к constantsработает в принципе как letРазница лишь в том, что их значение не может быть изменено. В константах мутация разрешена, но переназначение не допускается.

const foo = {};
foo.bar = 42;
console.log(foo.bar); //works

const name = []
name.push("Vinoth");
console.log(name); //works

const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.

console.log(age);

Если константа относится к object, это всегда будет относиться к object но object сам может быть изменен (если он изменчив). Если вам нравится иметь неизменный objectВы могли бы использовать Object.freeze([])

Согласно ECMAScript® 2021

Объявления Let и Const

  • Объявления let и const определяют переменные, которые ограничены LexicalEnvironment текущего контекста выполнения.
  • Переменные создаются при создании экземпляра содержащейся в них записи среды, но к ним нельзя получить доступ каким-либо образом до тех пор, пока не будет вычислена LexicalBinding переменной.
  • Переменной, определенной LexicalBinding с инициализатором, присваивается значение AssignmentExpression ее инициализатора при оценке LexicalBinding, а не при создании переменной.
  • Если LexicalBinding в объявлении let не имеет инициализатора, переменной присваивается значение undefined при оценке LexicalBinding.

Блокировать создание объявления

  • Когда блок или CaseBlock оценивается, создается новая декларативная запись среды, и привязки для каждой переменной, константы, функции или класса, объявленные в блоке, создаются в записи среды.
  • Независимо от того, как контроль покидает блок, LexicalEnvironment всегда возвращается в свое прежнее состояние.

Лексически объявленные имена верхнего уровня

На верхнем уровне функции или скрипта объявления функций обрабатываются как объявления var, а не как лексические объявления.

Вывод

  • let и const поднимаются, но не инициализируются.

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

Примеры ниже проясняют, как переменные "let" ведут себя в лексической / вложенной лексической области видимости.

Пример 1

var a;
console.log(a); //undefined

console.log(b); //undefined
var b;


let x;
console.log(x); //undefined

console.log(y); // Uncaught ReferenceError: y is not defined
let y; 

Переменная 'y' выдает ошибку referenceError, что не означает, что она не поднята. Переменная создается при создании экземпляра окружающей среды. Но к нему нельзя получить доступ, потому что он находится в недоступной "временной мертвой зоне".

Пример 2

let mylet = 'my value';
 
(function() {
  //let mylet;
  console.log(mylet); // "my value"
  mylet = 'local value';
})();

Пример 3

let mylet = 'my value';
 
(function() {
  let mylet;   
  console.log(mylet); // undefined
  mylet = 'local value';
})();

В примере 3 недавно объявленная переменная mylet внутри функции не имеет инициализатора перед оператором журнала, следовательно, значение undefined.

Источник

ECMAMDN

Из веб-документов MDN:

В ECMAScript 2015, let а также constподняты, но не инициализированы. Ссылка на переменную в блоке перед объявлением переменной приводит кReferenceError потому что переменная находится во "временной мертвой зоне" от начала блока до обработки объявления.

console.log(x); // ReferenceError
let x = 3;

В es6, когда мы используем let или const, мы должны объявить переменную перед их использованием. например. 1 -

// this will work
u = 10;
var u;

// this will give an error 
k = 10;
let k;  // ReferenceError: Cannot access 'k' before initialization.

например. 2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9

Я думаю, это может ответить на этот вопрос

В JavaScript переменные, объявленные с помощью и, поднимаются, но их поведение отличает их от переменных, объявленных с помощью .

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

  1. переменные поднимаются и инициализируются значением . Это означает, что вы можете ссылаться наvarпеременная до ее объявления, и она будет иметь значениеundefinedв таком случае.
      console.log(x); // undefined
var x = 10;
  1. и переменные также поднимаются, но не инициализируются. Если вы попытаетесь получить к ним доступ до их объявления, вы получите «ReferenceError».
      console.log(x); // ReferenceError: x is not defined
let x = 10;

Таким образом, хотя оба и подняты в начало области их блока или функции, вы не можете получить к ним доступ до их объявления из-за поведения временной мертвой зоны (TDZ). Такое поведение было введено для выявления потенциальных ошибок и повышения предсказуемости кода.

Временная мертвая зона (ВМЗ)

Временная мертвая зона (TDZ) — это концепция в JavaScript, связанная с объявлениями переменных с использованием ключевых слов и. Это этап в контексте выполнения JavaScript, который происходит во время инициализации переменной, прежде чем переменной будет присвоено значение. Понимание TDZ имеет решающее значение для предотвращения непредвиденного поведения вашего кода.

Вот как работает временная мертвая зона:

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

  2. Инициализация: переменные, объявленные с помощью TDZ, остаются неинициализированными до тех пор, пока им не будет присвоено значение с помощью=оператор.

  3. Доступ до инициализации. Попытка получить доступ к переменной, объявленной до того, как ей было присвоено значение, или сослаться на нее, приведет кReferenceError. Это связано с тем, что переменная существует в TDZ, и доступ к ней в этом состоянии запрещен.

Вот пример, иллюстрирующий временную мертвую зону:

      console.log(x); // Throws a ReferenceError

let x = 10;

В этом примере мы пытаемся зарегистрировать значение до его объявления и инициализации. Это приводит к ошибке ReferenceError, поскольку находится во временной мертвой зоне в точкеconsole.log()заявление.

Чтобы избежать временной мертвой зоны, рекомендуется всегда объявлять переменные в верхней части текущей области перед их использованием. Например:

      let x; // Declare x at the top of the scope
console.log(x); // undefined (no error)

x = 10; // Initialize x

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

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

let и const также поднимаются. Но будет выдано исключение, если переменная, объявленная с помощью let или const, будет прочитана до ее инициализации по причинам, указанным ниже.

  • В отличие от var, они не инициализируются значением по умолчанию при подъеме.
  • Они не могут быть прочитаны/записаны, пока они не будут полностью инициализированы.
  • В случае var, после создания определений переменных, перед выполнением построчно каждая из переменных инициализируется с неопределенным значением.
  • В случае let/const, инициализация в undefined не происходит до тех пор, пока не произойдет строка, на которой фактически происходит объявление.

Доступ к переменной до инициализации приводит к ReferenceError. Переменная находится во "временной мертвой зоне" от начала блока до обработки инициализации.

console.log(typeof name); // No intialization till now => referenceError
let name = "John";

Другие вопросы по тегам