Чи змінені в ES6 змінні, оголошені дозволом або const, не піднімаються?


266

Я деякий час грав з 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в цьому питанні?

Відповіді:


346

@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;оператор ініціалізує змінну з " undefinedlike" let y = undefined;.

Тимчасова мертва зона не є синтаксичним розташування, а , скоріше, час між змінним (масштабом) створенням та ініціалізацією. Це не помилка посилання на змінну в коді над декларацією, доки цей код не виконаний (наприклад, тіло функції або просто мертвий код), і він викине виняток, якщо ви отримаєте доступ до змінної перед ініціалізацією, навіть якщо доступ код знаходиться нижче декларації (наприклад, у оголошенні функції з піднімаються функціями, яке викликається занадто рано).

Чи є різниця між letі constв цьому питанні?

Ні, вони працюють так само, наскільки піднімаються. Єдина відмінність між ними полягає в тому, що constмураха повинна бути і може бути призначена лише в ініціалізаторській частині декларації ( const one = 1;як const one;і наступні перепризначення, такі one = 2як недійсні).

1: varДекларації все ще працюють лише на рівні функції, звичайно


16
Я вважаю, що щось подібне let foo = () => bar; let bar = 'bar'; foo();ілюструє всі декларації з ефектом піднімання ще краще, оскільки це не очевидно через часову мертву зону.
колба Естуса

1
Я збирався запитати про посилання на визначення хай у функції, оголошеній до пуску (тобто закриття). Я думаю, що це відповідає на питання, це законно, але буде помилка ref, якщо функція буде викликана перед виконанням оператора let, і буде добре, якщо функція буде викликана після цього. можливо, це можна було б додати до відповіді, якщо це правда?
Майк Ліпперт

2
@MikeLippert Так, це правильно. Ви не повинні викликати функцію, яка звертається до змінної до її ініціалізації. Цей сценарій трапляється, наприклад, з кожним оголошенням піднятих функцій.
Бергі

1
Рішення зробити constподібне let- вада дизайну. У межах сфери дії constслід було підняти та підключити щойно вчасно під час доступу до неї. Дійсно, у них повинно бути а const, а letта інше ключове слово, яке створює змінну, яка працює як "лише для читання" let.
Pacerier

1
" Перші ініціалізуються з невизначеним ..." може бути нормальним для оголошень var, але не здається відповідним для декларацій функції, яким призначається значення до початку виконання.
RobG

87

Цитуючи специфікацію ECMAScript 6 (ECMAScript 2015) letтаconst розділ декларацій ,

Змінні створюються тоді, коли міститься лексичне середовище екземпляра, але не може бути доступним жодним чином, доки не буде оцінено LexicalBinding змінної .

Отже, щоб відповісти на ваше запитання, так letі constпіднімайте, але ви не можете отримати доступ до них до того, як фактична декларація буде оцінена під час виконання.


22

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);

Якщо константа посилається на an object, вона завжди буде посилатися на objectале, але objectсама може бути змінена (якщо вона є змінною). Якщо ви хочете мати непорушний object, ви можете використовуватиObject.freeze([])


5

З веб-документів MDN:

У ECMAScript 2015 letі constпіднімаються, але не ініціюються. Посилання на змінну в блоці до оголошення змінної призводить до того, ReferenceErrorщо змінна знаходиться у "тимчасовій мертвій зоні" від початку блоку до обробки декларації.

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

0

у 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.