1. 作用域
let關(guān)鍵字:引入let的原因正是var的局限性.
相比于var,let有以下幾點優(yōu)勢:
-
作用域
var的作用域是會提升的,var聲明的變量只能是全局的或者是整個函數(shù)塊的let則允許聲明一個作用域被限制在塊級中的變量、語句或者表達式。
for(var i = 0; i < 5; i++) {
}
console.log(i);
分析 :上面代碼中使用的是var ,那么最終的輸出結(jié)果將會是5,因為i雖然在for循環(huán)中聲明的,但是作用域會被提升到函數(shù)塊的邊界或者直至全局
? 但是如果使用的是let,將會報錯,會顯示i is not define ,原因就是聲明的i被局限于for循環(huán)這個塊中,出了這個快就找不到i這個變量了,
? 需要注意的是,在程序的頂層,let并不會向var那樣將聲明的變量加到全局對象上去
//控制臺輸出結(jié)果
var i = 5
undefined
this.i
5
let y = 5
undefined
this.y
undefined
2. 對let塊級作用域的使用:
如果使用var來定義循環(huán)控制變量
var lis = document.querySelector('.test').querySelectorAll('li');
for(var i = 0 ; i<lis.length;i++){
lis[i].onclick = function (event) {
console.log(i);
}
}
//點擊li時輸出的結(jié)果都相同的,都是li的個數(shù)
如果使用let
var lis = document.querySelector('.test').querySelectorAll('li');
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function (event) {
console.log(i);
}
}
//點擊li時輸出的結(jié)果,為每個li標(biāo)簽的索引
分析
? var 聲明的i對應(yīng)的是全局變量,也就是說i是在循環(huán)之外存在的。所以每次點擊都對應(yīng)同一個i,而i是全局的,所以在循環(huán)結(jié)束后,i的值就已經(jīng)確定了,因此每次點擊出來的都是一樣的。
? 但是如果使用了let,那么使用的將是塊級作用域,也就是說,每個點擊事件都會進入一個不同的塊,所以每個點擊都會輸出正確的序號。
模仿私有接口
在處理構(gòu)造函數(shù)的時候,可以通過let綁定來共享一個或多個私有成員,而不使用閉包
let暫存死區(qū)的錯誤
在相同的函數(shù)或塊作用域內(nèi)重新聲明同一個變量會引發(fā)SyntaxError
if (x) {
let foo;
let foo; // TypeError thrown.
}
//重復(fù)聲明報錯,但是var不會報錯
let在包含聲明的作用域頂部被創(chuàng)建,通常這種被叫做“變量提升”。但和var不同的是,var的創(chuàng)建會設(shè)置一個初試的undefined值,let變量在沒有運行到聲明代碼時是不會被初始化的。引用它將會導(dǎo)致 ReferenceError(而使用 var聲明變量則恰恰相反,該變量的值是 undefined )。直到初始化執(zhí)行的時候,該變量都處于從塊開始到初始化處理的“暫存死區(qū)”。
//暫存死區(qū)
function do_something() {
console.log(x); //undefined
console.log(y); //Uncaught ReferenceError: y is not defined
var x = 1;
let y = 2;
}
do_something();
在 switch 聲明中你可能會遇到這樣的錯誤,因為它只有一個塊.
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
但是,重要的是要指出嵌套在case子句內(nèi)的塊將創(chuàng)建一個新的塊作用域的詞法環(huán)境,這不會產(chǎn)生上面顯示的重新聲明錯誤。
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
與詞法作用域結(jié)合的暫存死區(qū)
由于詞法作用域,表達式(foo + 55)內(nèi)的標(biāo)識符“foo”會解析為if塊的foo,而不是覆蓋值為33的foo。在這一行中,if塊的“foo”已經(jīng)在詞法環(huán)境中創(chuàng)建,但尚未達到(并終止)其初始化(這是語句本身的一部分):它仍處于暫存死區(qū)。
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
這種現(xiàn)象可能會使您陷入以下情況。指令let n of n.a已經(jīng)在for循環(huán)塊的私有范圍內(nèi),因此標(biāo)識符“n.a”被解析為位于指令本身的第一部分(“l(fā)et n”)中的'n'對象的屬性'a' ,由于尚未達成和終止其聲明,因此仍處于暫存死區(qū)。
function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]}
for (let n of n.a) { // ReferenceError
console.log(n);
}
}
go({a: [1, 2, 3]});
其他情況
當(dāng)在塊中使用時,let將變量的作用域限制為該塊。注意**var**的作用域在它被聲明的函數(shù)內(nèi)的區(qū)別。
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2