ES6 也許你并沒有完全了解let const


很早就想系統的學習ES6了,奈何資源太少,把阮一峰大大的ES6通讀了一遍之后發現,沒有一點感覺,是的,也許是自己的積累或者境界不夠,真的一點感覺都沒有,市面上可供學習參考的ES6的資料真的不多。之前一直盼望著《你不知道的JavaScript下卷》出中文版,聽說到11月份,好吧。一次偶然,得知《高程》的作者也寫了ES6的書,雖然中文版也沒有,但是真的忍不了的,用我的渣英語慢慢讀吧。


問在前面:

  1. 問:用const申明的對象是可以修改的,對嗎?
    答:對。
  2. let申明的全局變量和用var申明的全局變量是一樣的嗎?
    答:不一樣。

如果你都回答上了,并且知其然知其所以然,那你掌握的很好,不用往下看了。如果并不知道,那就一起慢慢回顧一下吧。


var###

相信大家已經對letconst有了一定的認識,也應該了解了塊級作用域以及var并沒有塊級作用域。
var沒有塊級作用域,具體體現在兩個方面,一個是在函數中,一個是在循環中。具體例子如下:

var foo = function(a){
    if(a){
        var text = 'hello'
        return text
    }else{
        //這里也有text,其值為undefined
        return null
    }
        //這里也有text,其值為undefined
}

其實我們的本意是希望如下代碼:

var foo = function(a){
    if(a){
        var text = 'hello'
        return text
    }else{
        //這里text未定義
        return null
    }
        //這里text未定義
}

但是因為var沒有塊級作用域,并且存在變量提升,所以上述代碼其實與下面的寫法相同:

var foo = function(a){
     var text;undefined
    if(a){
        text = 'hello'
        return text
    }else{
        //這里也有text,其值為undefined
        return null
    }
        //這里也有text,其值為undefined
}

這是在函數中,其實在循環中也一樣,比如:

for (var i = 0; i < 10; i++) {
   //做一些操作
}
//在外部,i依舊存在,并且會輸出10
console.log(i); //10           

很明顯,這是不符合常理的,我們期望的是:

for (var i = 0; i < 10; i++) {
   //做一些操作
}
//在外部,i未定義,報錯
console.log(i); //ReferenceError       

然而并做不到。再來舉個例子,在循環中的函數,使用var,是更加糟糕,更加不符合常理的,比如:

var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
    func();     // 輸出10,10次
});

其實我們期待的是,輸出0-9,但是事與愿違,輸出了10次10,為什么?因為for循環中的每一次迭代都共享著用var申明的i,也就是說,這里的i不是分別在單獨的作用域里,而是在同一個作用域里,是一個值,而不是十個,所以,一改全改,最終變成輸出10個10。
如何解決這個問題呢?
在ES5中,我們想到了用立即執行函數(IIFE),把每一個i的作用域分開。具體代碼如下:

var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
            console.log(value);
        }
    }(i)));
}
funcs.forEach(function(func) {
    func();     //輸出0-9
});

解決了,但是顯得太臃腫而且難以理解是不是?是的,一眼看過去很難看清楚這個函數到底想表達什么。但是,如果我們一開始就使用let代替var,這個世界就變得簡單而清爽了:

var funcs = [];
for (let i = 0; i < 10; i++) {
    funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
    func();     // 輸出0-9
});

符合邏輯,并且代碼清爽。完美!這就是let的魅力。下面讓我們來深入學習let

let###

letvar差不多,只不過申明的變量有塊級作用域,并且不會變量提升。什么意思呢,就是說,let聲明的變量只存在于聲明它的{}內部,或者循環體內部,或者函數內部,外部是不存在的。因為沒有變量提升,所以有暫時性死區。

暫時性死區

這個概念我覺得是相對于var來說的,因為var有變量提升,所以先使用后申明是不會報錯的,而是undefined,具體例子如下:

console.log(value);//undefined
var value = 10;
console.log(value);//10

其實這個就等于:

var value;
console.log(value);//undefined
value = 10;
console.log(value);//10

說起變量提升,在這里想多說一點。函數用function (){····}這樣的方式,也有提升,而且是在變量提升之前,也就是說,提升的優先級比變量提升高,并且同名函數,后者會覆蓋前者。也就是說:

foo();//b
function foo(){
    console.log('a')
}
function foo(){
    console.log('b')
}

扯遠了,現在回到let的暫時性死區,和var不同,請看下面的例子:

console.log(value);//報錯,ReferenceError
let value = 10;
console.log(value);

因為沒有變量提升,所以只能先聲明,后使用。
還要一些小細節,在同一作用域中var申明變量是可以重復申明的,并且后者覆蓋前者:

var a = 10;
var a = 20;
console.log(a)//20

而在let中,這樣會報錯:

let a = 10;
let a = 20;//報錯,語法錯誤,a已經申明過了。
console.log(a)

當然,這樣也不行:

var a = 10;
let a = 20;//報錯,語法錯誤,a已經申明過了。
console.log(a)

不同作用域,是可以的:

var a = 10;
function foo(){
   let a = 20;//合法
}

回到最初提出的問題之一,全局作用域下,letvar申明的變量的不同之處
首先,var大家都了解,全局申明的變量在瀏覽器中就是window的屬性,比如:

var a = 10;
window.a//10

給個最直觀的例子:

let a = 10;
window.a//報錯,a未定義

明顯可以看出,這是不同的。下面,我們來深入一點,用var申明全局變量并沒有想象中的那么好,比如:

var RegExp = 10;
window.RegExp//10

是不是賊恐怖?把內置對象給干掉了,如此危險的操作,肯定是要避免的。用let就可以避免了:

let RegExp = 10;
window.RegExp//function RegExp() { [native code] }
console.log(RegExp )//10

為什么會這樣?因為用var申明的全局變量會當做window對象的屬性,而let申明的全局變量,只是一個變量,不會當做window對象的屬性。let就說的差不多了,下面說下const

const###

其實constlet用法,包括需要注意的一些細節都一樣比如沒有變量提升,聲明全局變量,塊級作用域。但是還是有一些地方是不一樣的,最大的一個不同點就是,const申明的變量,是不可變的,變了就報錯,具體例子如下:

const a =10;
a = 20;//報錯,語法錯誤,不能改變一個不變的值

當然,如果你以為這就是const的全部,那就不太好了,回到開頭提出的問題,如果用const申明的是一個對象呢?

const a = {
    value:10
}
a.value = 20;
a;//a{value:20}

修改了,沒有報錯。可能你有些動搖了,那么看下一個例子:

const a = {
    value:10
}
a = {
    value:20//報錯,語法錯誤,不能改變一個不變的值
}

結論,const只會鎖定變量的地址,而不會鎖定變量的屬性,這個和Object.freeze(),還是有很大的區別的。

總結###

  • var沒有想象中的那么好,盡量使用letconst代替。
  • var申明的全局變量,除非你真的是想給window對象加屬性,不然就用letconst代替。
  • letconst沒有變量提升,注意暫時性死區。
  • const并不是真正的鎖定變量的所有,而只是鎖定這個變量的地址而已,所以這個變量的屬性是可以修改的。
  • 盡量都是用const,除非你確定你申明的是一個變量(會變的值)。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容