ES6 已經出現很長時間了,可是做爲一個初學者,仍然要仔細深刻的理解這些點,接下來我會寫一個 ES6 語法系列,深刻講解 ES6 語法產生的背景與用法,但願能給你們帶來幫助。javascript
你們都清楚,在 let 聲明方式出現以前,咱們聲明一個變量只能經過 var 來定義。html
var a = 1;
var b = 'zhangsan';
var c = {name: 1, age: 2};
var d = function(){}
var e = [];
複製代碼
一切都很正常,直到有一天,咱們寫出了這樣的代碼:java
for(var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
});
}
複製代碼
咱們指望使用這種代碼,獲得以下結果:閉包
0
1
2
3
4
5
6
7
8
9
複製代碼
但事實卻很打臉,獲得的結果以下:函數
這和咱們的指望結果不太同樣,爲何會獲得這樣的結果呢? 在此能夠說明一下,雖然有些跑題。ui
緣由有二:spa
一個緣由是: var 定義的變量不受塊級做用域的限制。code
另外一個緣由是:JavaScript 引擎的事件循環機制在起做用。for循環的同步任務執行完畢以後,纔會將從事件隊列中取出回調函數,放到調用棧中執行:即setTimeout
的回調函數。因此當同步任務執行完以後, i 的值已經變爲了 10,此時,10 個定時器的回調開始執行,打印出 10 個 10。cdn
那麼,聰明的同窗開始想辦法了,利用 JS 中的閉包特性,實現指望的結果:htm
for(var i = 0; i < 10; i++){
(function(t){
setTimeout(function(){
console.log(t);
});
})(i);
}
複製代碼
能夠看到,代碼不易理解,書寫也很麻煩。
因而 ES6 中新出現了 let 聲明方式, 經過 let 聲明的變量有了塊級做用域
的限制。 舉個例子,先從 var 聲明開始:
{
var a = 1;
}
console.log(a);
複製代碼
因爲 var 沒有塊級做用域的約束,因此咱們在塊級做用域之外訪問 a 變量的話,仍然是可以訪問到的。所以,上面的定時器例子會打印出 10 個 10。
接下來,咱們將 var 改成 let 進行聲明:
{
let a = 1;
}
console.log(a);
複製代碼
你們能夠猜想一下輸出結果是什麼?
事實上會報錯的:
緣由在於,按照 let 的聲明特色, let 定義的變量只能在聲明時所在的做用域中訪問到,因此 a 被限制在了塊級做用域中,在塊級做用域外訪問 a 的話,因爲外層做用域並未定義 a 變量,因此會報上述錯誤。
既然 let 有了塊級做用域的約束,咱們就能夠用 let 來改寫上面的定時器例子:
for(let i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
});
}
複製代碼
之前咱們用 var 聲明變量的時候,能夠重複進行同名變量的聲明,好比
var a = 1;
var a = 2;
var a = 3;
console.log(a);
複製代碼
以上代碼,沒有報錯,而且 a 的值獲得了篡改,以最後一次賦值爲準。
你們可能以爲沒有問題。
咱們再舉一個例子:
假設 A、B、C 三個同窗須要共同完成一個頁面功能。
A同窗寫了一個 JS 文件 A.js,A同窗再這個文件中定義了一個name變量,賦值爲章三
,他想在頁面上將這個名字打印出來。
var name = '章三';
複製代碼
B同窗寫了另外一個 JS 文件 B.js,也定義了一個變量,也叫 name,賦值爲 '李四',他也想在頁面上將這個名字打印出來。
var name = '李四';
複製代碼
A 同窗告訴 C 同窗,取出 name 屬性,打印出來就能夠了。
B 同窗告訴 C 同窗,取出 name 屬性,打印出來就能夠了。
惋惜 C 同窗不是一個細心的同窗,他沒有意識到兩個變量重名了,因而他寫了一個 html 文件,引入了 A.js 和 B.js,而後將 name 屬性打印出來。
<script src="./A.js"></script>
<script src="./B.js"></script>
<body>
<div id="name"> </div>
<script> document.querySelector('#name').innerHTML = name; </script>
</body>
複製代碼
而後 C 同窗將頁面發給 A 同窗和 B 同窗,讓他們看一下結果對不對。
結果 A 同窗一看,發現打印出來的名字不是章三
,而是 李四
,他就怒氣衝衝地去質問 C 同窗,C同窗說,我就是按照你告訴個人方式去打印的呀。
故事進行到這裏,矛盾出現了:
同一個做用域下定義多個重名變量,JavaScript 引擎不會報錯,可是會爲程序的正確性帶來隱患。
幸運的是,let 的出現很好地解決了這個問題:
同一個做用域下,let 聲明的變量不能和已經聲明的變量重名,不然引擎會報錯。
let a = 1;
let a = 2;
複製代碼
或者
var a = 1;
let a = 2;
複製代碼
因此,let 爲咱們帶來了另外一個好處,防止咱們定義重名變量。
仍然以一段代碼爲例:
console.log(a);
var a = 1;
複製代碼
你們猜想一下,輸出結果是什麼?
程序運行不會報錯,可是輸出結果不是咱們指望的。
輸出結果是 undefined。
因此,這會帶來一個問題,咱們在聲明一個變量以前,萬一使用了這個變量,就會獲得意料以外的結果,進而形成程序運行錯誤,若是能有一種機制可以強制咱們在使用變量以前,必須先聲明該變量就行了。
這就是 let 的第三個特色:使用一個 let 聲明的變量以前,必須先聲明。
console.log(a);
let a = 1;
複製代碼
如上代碼,a 變量的聲明放在了使用以後,咱們看下運行結果:
編譯器給出了報錯提示,這促使咱們在早期就能發現問題。
以上就是 ES6 的 let 使用總結,雖然是一個很小的點,可是咱們也要認清它出現的背景,爲了解決什麼問題而生。
以後,會爲你們帶來 ES6 其餘語法的剖析,敬請期待~~~