var用的好好的挪鹏,為甚es6要出let和const去替換var呢袁辈?
當然是為自己曾經(jīng)犯下的錯誤買單!
在講解es6之前挖滤,我們必須要提一下es5中的var,也就是曾經(jīng)的那個錯誤浅役。
if (condition) {
? ? var value = 1;
}
console.log(value);
很簡單的分析一下斩松,初學者可能會認為,只有在condition為true的時候担租,value才會被賦值砸民,如果condition為false的時候,代碼應該會報錯才對奋救!但是岭参,事實并不是這樣的,瀏覽器在執(zhí)行這段代碼的時候尝艘,并不是這么解析的演侯!
var value;
if (condition) {
? ? value = 1;
}
console.log(value);
這樣,很容易我們就能判斷出背亥,如果condition的值為false的時候秒际,console出來的值為undefined悬赏。
原因就是我們常說的,變量提升娄徊。
為了加強對變量生命周期的控制闽颇,ECMAScript 6 引入了塊級作用域。
塊級作用域存在于:
? ? ? ?1.函數(shù)內(nèi)部
? ? ? ? 2.塊中(字符 { 和 } 之間的區(qū)域)
引出我們今天的主角—let和const
let 和 const 都是塊級聲明的一種寄锐。
let和const的特點:
1.不會被提升(真的是這樣的嗎兵多?)
if ( condition ) {
? ? let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined?
在代碼塊外面訪問,直接判定橄仆,為定義剩膘。
2.重復聲明報錯
var value = 1;
let value = 2; // Uncaught SyntaxError: Identifier 'value' has already been declared
這在以前是完全可以的,后定義的回覆蓋以前的盆顾。
3.不綁定全局作用域
let value = 1 ;
console.log(window.value);
const也是相同的怠褐,都訪問不到。
const和let的區(qū)別:
const 用于聲明常量您宪,其值一旦被設(shè)定不能再被修改奈懒,否則會報錯。
值得一提的是:const 聲明不允許修改綁定蚕涤,但允許修改值筐赔。這意味著當用 const 聲明對象時:
const data = {
? ? value: 1
}
// 沒有問題
data.value = 2;
data.num = 3;
// 報錯
data = {}; // Uncaught TypeError: Assignment to constant variable.
臨時死區(qū)
臨時死區(qū)(Temporal Dead Zone),簡寫為 TDZ揖铜。
let 和 const 聲明的變量不會被提升到作用域頂部,如果在聲明之前訪問這些變量达皿,會導致報錯天吓。
console.log(typeof value); // Uncaught ReferenceError: value is not defined
let value = 1;
來個例子?
var value = "global";
// 例子1
(function() {
? ? console.log(value);
? ? let value = 'local';
}());
// 例子2
{
? ? console.log(value);
? ? const value = 'local';
};
結(jié)果是:都報錯了B鸵A淠!
這是因為 JavaScript 引擎在掃描代碼發(fā)現(xiàn)變量聲明時汤功,要么將它們提升到作用域頂部(遇到 var 聲明)物邑,要么將聲明放在 TDZ 中(遇到 let 和 const 聲明)。訪問 TDZ 中的變量會觸發(fā)運行時錯誤滔金。只有執(zhí)行過變量聲明語句后色解,變量才會從 TDZ 中移出,然后方可訪問餐茵。
循環(huán)中的塊級作用域
var funcs = [];
for (var i = 0; i < 3; i++) {
? ? funcs[i] = function () {
? ? ? ? console.log(i);
? ? };
}
funcs[0](); // 3
如何改變現(xiàn)狀呢科阎?我要的是funcs[0]() == 0
在沒有es6 之前,這個事兒麻煩了忿族,還得使用閉包的方式锣笨!
var funcs = [];
for (var i = 0; i < 3; i++) {
? ? funcs[i] = (function(i){
? ? ? ? return function() {
? ? ? ? ? ? console.log(i);
? ? ? ? }
? ? }(i))
}
funcs[0](); // 0
ES6 的 let 為這個問題提供了新的解決方法:
var funcs = [];
for (let i = 0; i < 3; i++) {
? ? funcs[i] = function () {
? ? ? ? console.log(i);
? ? };
}
funcs[0](); // 0
問題在于蝌矛,上面講了 let 不提升,不能重復聲明错英,不能綁定全局作用域等等特性入撒,可是為什么在這里就能正確打印出 i 值呢?
如果是不重復聲明椭岩,在循環(huán)第二次的時候衅金,又用 let 聲明了 i,應該報錯呀簿煌,就算因為某種原因氮唯,重復聲明不報錯,一遍一遍迭代姨伟,i 的值最終還是應該是 3 呀惩琉,還有人說 for 循環(huán)的 設(shè)置循環(huán)變量的那部分是一個單獨的作用域,就比如:
for (let i = 0; i < 3; i++) {
? let i = 'abc';
? console.log(i);
}
// abc
// abc
// abc
這個例子是對的夺荒,如果我們把 let 改成 var 呢瞒渠?
for (var i = 0; i < 3; i++) {
? var i = 'abc';
? console.log(i);
}
// abc
經(jīng)查, for 循環(huán)中使用 let 和 var技扼,底層會使用不同的處理方式伍玖。
簡單的來說,就是在?for (let i = 0; i < 3; i++)?中剿吻,即圓括號之內(nèi)建立一個隱藏的作用域窍箍,這就可以解釋為什么:
for (let i = 0; i < 3; i++) {
? let i = 'abc';
? console.log(i);
}
// abc
// abc
// abc
然后每次迭代循環(huán)時都創(chuàng)建一個新變量,并以之前迭代中同名變量的值將其初始化丽旅。
var funcs = [];
for (let i = 0; i < 3; i++) {
? ? funcs[i] = function () {
? ? ? ? console.log(i);
? ? };
}
funcs[0](); // 0
相當于:
// 偽代碼
(let i = 0) {
? ? funcs[0] = function() {
? ? ? ? console.log(i)
? ? };
}
(let i = 1) {
? ? funcs[1] = function() {
? ? ? ? console.log(i)
? ? };
}
(let i = 2) {
? ? funcs[2] = function() {
? ? ? ? console.log(i)
? ? };
};
到此椰棘,我們就講完了嗎?沒有榄笙,并沒有邪狞,上面還有個提升的問題么!
首先明確一點:提升不是一個技術(shù)名詞茅撞。
要搞清楚提升的本質(zhì)帆卓,需要理解 JS 變量的「創(chuàng)建create、初始化initialize 和賦值assign」
假設(shè)有如下代碼:
function fn(){
? var x = 1
? var y = 2
}
fn()
在執(zhí)行 fn 時米丘,會有以下過程(不完全):
進入 fn剑令,為 fn 創(chuàng)建一個環(huán)境。
找到 fn 中所有用 var 聲明的變量蠕蚜,在這個環(huán)境中「創(chuàng)建」這些變量(即 x 和 y)尚洽。
將這些變量「初始化」為 undefined。
開始執(zhí)行代碼
x = 1 將 x 變量「賦值」為 1
y = 2 將 y 變量「賦值」為 2
也就是說 var 聲明會在代碼執(zhí)行之前就將「創(chuàng)建變量靶累,并將其初始化為 undefined」腺毫。
這就解釋了為什么在 var x = 1 之前 console.log(x) 會得到 undefined癣疟。
接下來來看 function 聲明的「創(chuàng)建、初始化和賦值」過程
假設(shè)代碼如下:
fn2()
function fn2(){
? console.log(2)
}
JS 引擎會有一下過程:
找到所有用 function 聲明的變量潮酒,在環(huán)境中「創(chuàng)建」這些變量睛挚。
將這些變量「初始化」并「賦值」為 function(){ console.log(2) }。
開始執(zhí)行代碼 fn2()
也就是說 function 聲明會在代碼執(zhí)行之前就「創(chuàng)建急黎、初始化并賦值」扎狱。
接下來看 let 聲明的「創(chuàng)建、初始化和賦值」過程
假設(shè)代碼如下:
{
? let x = 1
? x = 2
}
我們只看 {} 里面的過程:
找到所有用 let 聲明的變量勃教,在環(huán)境中「創(chuàng)建」這些變量
開始執(zhí)行代碼(注意現(xiàn)在還沒有初始化)
執(zhí)行 x = 1淤击,將 x 「初始化」為 1(這并不是一次賦值,如果代碼是 let x故源,就將 x 初始化為 undefined)
執(zhí)行 x = 2污抬,對 x 進行「賦值」
這就解釋了為什么在 let x 之前使用 x 會報錯:
let x = 'global'
{
? console.log(x) // Uncaught ReferenceError: x is not defined?
? let x = 1
}
原因有兩個:
1.console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
2.執(zhí)行 log 時 x 還沒「初始化」绳军,所以不能使用(也就是所謂的暫時死區(qū))
看到這里印机,你應該明白了 let 到底有沒有提升:
let 的「創(chuàng)建」過程被提升了,但是初始化沒有提升门驾。
var 的「創(chuàng)建」和「初始化」都被提升了射赛。
function 的「創(chuàng)建」「初始化」和「賦值」都被提升了。
最后看 const奶是,其實 const 和 let 只有一個區(qū)別楣责,那就是 const 只有「創(chuàng)建」和「初始化」,沒有「賦值」過程诫隅。
這四種聲明腐魂,用下圖就可以快速理解:
所謂暫時死區(qū),就是不能在初始化之前逐纬,使用變量。
至此削樊,我們結(jié)束今天的分享...
引用:https://juejin.im/post/5b0238f66fb9a07aca7a74ba
? ? ? ? ? ?https://zhuanlan.zhihu.com/p/28140450