在es6中,let關鍵字用於宣告變數;但是所宣告的變數,只在let指令所在的程式碼區塊內有效。 let不會發生「變數提升」現象,所以變數一定要在宣告後使用,否則報錯。只要區塊級作用域內存在let指令,它所宣告的變數就「綁定」(binding)這個區域,就不再受外部的影響。
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
ES6新增了let
指令,用來宣告變數。它的用法類似於var
,但是所聲明的變量,只在let
命令所在的程式碼區塊內有效。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
上面程式碼在程式碼區塊之中,分別用let
和var
宣告了兩個變數。然後在程式碼區塊之外呼叫這兩個變量,結果let
聲明的變數報錯,var
聲明的變數傳回了正確的值。這表明,let
聲明的變數只在它所在的程式碼區塊有效。
for
循環的計數器,就很適當地使用let
指令。
for (let i = 0; i < 10; i++) {} console.log(i); //ReferenceError: i is not defined
上面程式碼中,計數器i
只在for
循環體內有效,在循環體外引用就會報錯。
下面的程式碼如果使用var
,最後輸出的是10。
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
上面程式碼中,變數i
是var
宣告的,在全域範圍內都有效。所以每一次循環,新的i
值都會覆寫舊值,導致最後輸出的是最後一輪的i
的值。
如果使用let
,宣告的變數只在區塊層級作用域內有效,最後輸出的是6。
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
上面程式碼中,變數i
是let
宣告的,目前的i
只在本輪迴圈有效,所以每一次迴圈的i
其實都是新的變量,所以最後輸出的是6。
let
不像var
那樣會發生「變數提升」現象。所以,變數一定要在宣告後使用,否則報錯。
console.log(foo); // 输出undefined console.log(bar); // 报错ReferenceError var foo = 2; let bar = 2;
上面程式碼中,變數foo
用var
指令聲明,會發生變數提升,也就是腳本開始執行時,變數foo
已經存在了,但是沒有值,所以會輸出undefined
。變數bar
用let
指令聲明,不會發生變數提升。這表示在宣告它之前,變數bar
是不存在的,這時如果用到它,就會拋出錯誤。
只要在區塊層級作用域內存在let
指令,它所宣告的變數就「綁定」(binding)這個區域,不再受外在的影響。
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; }
上面程式碼中,存在全域變數tmp
,但是區塊級作用域內let
又宣告了一個局部變數tmp
,導致後者綁定這個區塊級作用域,所以在let
宣告變數前,對tmp
賦值會報錯。
ES6明確規定,如果區塊中存在let
和const
命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
總之,在程式碼區塊內,使用let指令宣告變數之前,變數都是不可用的。這在語法上,稱為「暫時性死區」(temporal dead zone,簡稱TDZ)。
if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ结束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
上面程式碼中,在let
指令宣告變數tmp
之前,都屬於變數tmp
的「死區」。
「暫時性死區」也意味著typeof
不再是百分之百安全的操作。
typeof x; // ReferenceError let x;
上面程式碼中,變數x
使用let
指令聲明,所以在宣告之前,都屬於x
的“死區”,只要用到該變數就會報錯。因此,typeof
運行時就會拋出一個ReferenceError
。
作為比較,如果一個變數根本沒有被聲明,使用typeof
反而不會報錯。
typeof undeclared_variable // "undefined"
上面程式碼中,undeclared_variable
是一個不存在的變數名,結果回傳「undefined」。所以,在沒有let
之前,typeof
運算子是百分之百安全的,永遠不會報錯。現在這一點不成立了。這樣的設計是為了讓大家養成良好的程式設計習慣,變數一定要在聲明之後使用,否則就報錯。
有些「死區」比較隱蔽,不太容易發現。
function bar(x = y, y = 2) { return [x, y]; } bar(); // 报错
上面代码中,调用bar
函数之所以报错(某些实现可能不报错),是因为参数x
默认值等于另一个参数y
,而此时y
还没有声明,属于”死区“。如果y
的默认值是x
,就不会报错,因为此时x
已经声明了。
function bar(x = 2, y = x) { return [x, y]; } bar(); // [2, 2]
ES6规定暂时性死区和let
、const
语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在ES5是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
let不允许在相同作用域内,重复声明同一个变量。
// 报错 function () { let a = 10; var a = 1; } // 报错 function () { let a = 10; let a = 1; }
因此,不能在函数内部重新声明参数。
function func(arg) { let arg; // 报错 } function func(arg) { { let arg; // 不报错 } }
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = "hello world"; } } f(); // undefined
上面代码中,函数f执行后,输出结果为undefined
,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
let
实际上为JavaScript新增了块级作用域。
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
上面的函数有两个代码块,都声明了变量n
,运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var
定义变量n
,最后输出的值就是10。
ES6允许块级作用域的任意嵌套。
{{{{{let insane = 'Hello World'}}}}};
上面代码使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。
{{{{ {let insane = 'Hello World'} console.log(insane); // 报错 }}}};
内层作用域可以定义外层作用域的同名变量。
{{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}};
块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... }
【相关推荐:javascript视频教程、编程视频】
以上是es6中let怎麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!