本文首發於我的網站: let關鍵字:增強版的var關鍵字
你好,今天大叔想和你嘮扯嘮扯 ES6 新增的關鍵字 —— let
。再說 let
的具體用法以前,大叔想先和你說說大叔本身對 let
的感覺 —— let
其實就是增強版的 var
。爲啥這麼說呢?別急,且聽大叔慢慢道來。javascript
首先,let
和 var
的做用是同樣同樣滴,都是用來聲明變量。看到這兒,你可能會有個問題啦,既然做用同樣,爲啥還要再搞個什麼新特性出來?html
想要回答這個問題,就要說到 let
和 var
的不一樣之處了。比方說 var
聲明的全局變量會自動添加到頂級對象中做爲屬性,而 let
就不會。再比方說 var
容許聲明提高或者重複聲明,而 let
就不容許這樣作。固然了,它們之間的不一樣可不止這些,大叔也只是舉個栗子而已。前端
若是你沒了解過 ES6 的內容,看到這兒可能有點懵。不要緊啊~ 別往內心去,由於接下來大叔就是要和你嘮扯嘮扯 let
的具體用法。java
在整明白 let
和 var
第一點不一樣以前,大叔要先和你嘮扯嘮扯 var
這個關鍵字的一些用法。爲啥?!var
你要是都整不明白的話,你還想整明白 let
,那就是一個美麗的扯!安全
首先,我們都知道其實聲明一個全局變量,是既可使用 var
進行聲明,也能夠不使用 var
進行聲明的。比方說像下面這段代碼同樣:app
var a = 'a' console.log(a) b = 'b' console.log(b)
上面這段代碼不用大叔多扯,想必你也知道打印的結果是個啥 —— 打印 a 和 b 嘛。別急,這纔是個開始,咱不點慢慢來不是~ ecmascript
接下來呢,大叔要用 delete
這個運算符來作個騷操做了 —— 先用 delete
刪除上面的兩個變量 a
和 b
,而後呢再分別打印這兩個變量的值。函數
你尋思一下這個時候應該打印的結果是啥呢?對啦!變量 a
的值會正常輸出 a,但變量 b
會報錯 b is not defined
。那爲啥又是這樣一個結果吶?學習
大叔以爲你應該知道 delete
運算符的做用是用來刪除對象的屬性,可是 delete
是沒法刪除變量的。對啦!你想的沒錯,這就說明上面聲明的 a
是變量但不是對象的屬性,而是 b
是對象的屬性但不是變量。網站
大叔這話說的有點繞,給你帶入一個場景吧。好比上面這段代碼是在一個 HTML 頁面中定義的 JavaScript 代碼,那 a
就是一個全局變量,b
就是向 window
對象添加了一個屬性。因此,delete
運算符能夠刪除 b
,但不能刪除 a
的緣由了。
那也就是說使用 var
關鍵字聲明的是變量,不使用 var
關鍵字聲明的是 window
對象的屬性唄。話嘮叨這兒,大叔還得來個騷操做。咱再看一段代碼:
var a = 'a' console.log(window.a) var b = 'b' console.log(window.b)
這段代碼若是按照上面的結論,打印的結果就應該是 undefined 和 b。可是~ 你真實運行一下這段代碼,就應該知道實際上打印的結果是 a 和 b!
這咋和上面的結論不同呢?!是否是又有點懵?哈哈~ 別先急着懵逼,這個問題其實是 JavaScript 的做者 Brendan Eich 當年在設計 JavaScript 這門語言時的一個小失誤:在全局做用域中聲明的變量同時會被做爲屬性添加到頂級對象中。
可能嘮扯到這兒,你會滿屏的吐槽彈幕:這尼瑪誰不知道?!但大叔真正想和你嘮扯的就是這一點,這個小小的失誤,就致使了使用 var
關鍵字聲明的全局變量會污染全局對象的問題。
而 ES6 新增的 let
就很好滴彌補了這個問題!也就是說,使用 let
關鍵字聲明的全局變量不會污染全局對象。不信咱能夠來試試嘛~ 仍是剛纔那個場景,在一個 HTML 頁面中定義 JavaScript 代碼,僅僅把 var
改爲 let
:
let a = 'a' console.log(a) console.log(window.a)
這段代碼實際的運行結果就是 a 和 undefined。事實證實 let
有效滴解決了 var
的問題,因此你知道爲啥 ES6 要新增一個關鍵字來完成和 var
同樣的事兒了吧?!
可是,但但是,可可是~ let
就這麼一點點和 var
的區別嗎?答案確定不是滴。我們仍是先來嘮扯嘮扯 var
關鍵字,使用 var
聲明的變量是容許反覆滴重複聲明的,就像下面這段代碼:
var a = 'a' var a = 'aa' console.log(a)
這段代碼最終打印的結果是 aa,緣由就在於 var
聲明的變量是容許重複聲明的。可能這會兒你又會問了,這我也知道啊,有啥子問題嗎?
問題確定是有滴,要是沒有大叔花這麼多口舌和你在這兒叨逼叨幹啥啊~ 大叔仍是給你帶入一個場景,比方說你定義了一個 JS 文件是須要被其餘小夥伴導入使用滴,那你在這個文件裏面聲明的變量在人家那分分鐘被從新聲明瞭,你心裏是個啥感覺?
固然了,大叔就是舉個栗子,你也別太當真啦~ 總而言之,就是說我們在真實開發時對變量的命名確定是有規劃的,不能隨意就被從新聲明使用,這樣會讓命名空間很亂很亂滴。
你可能有想問了,這個問題要怎麼解決呢?答案其實很簡單,就是使用 ES6 新增的這個 let
關鍵字。由於 let
關鍵字聲明的變量是不容許被重複聲明,不然會報錯滴。不信你也能夠看看嘛:
let a = 'a' let a = 'aa' console.log(a)
僅僅只是把 var
改爲 let
,這個結果就是報錯了,報錯的內容是:SyntaxError: Identifier 'a' has already been declared
,大概的意思就是變量 a 已經被聲明過了。
因此,你看,let
可不是僅僅那麼一點點的區別呢!
這會兒你是否是又想問 let
和 var
之間還有沒有其餘區別啊?大叔也不藏着掖着了,乾脆一口氣都和你說了吧!你知道使用 var
關鍵字聲明的變量是容許聲明提早的嗎?啥?不知道!沒事兒,這個簡單,啥叫聲明提早,來看段代碼:
console.log(a) var a = 'a'
你運行一下這段代碼,看看打印的結果是啥?沒錯~ 結果就是 undefined。爲啥不是報錯呢?緣由就是使用 var
關鍵字聲明的變量容許聲明提早。仍是說人話吧,也就是說,上面這段代碼和下面這段代碼本質上是沒區別的:
var a console.log(a) a = 'a'
這樣嬸兒寫你可能就明白了爲啥打印的結果是 undefined 而不是報錯了吧!可是,嘿嘿~ 我們又得嘮扯嘮扯 let
了,由於 let
聲明的變量就不容許聲明提早。不信的話仍是給你看段代碼先:
console.log(a) let a = 'a'
這段代碼運行以後打印的結果就是報錯,報錯的內容是:ReferenceError: Cannot access 'c' before initialization
,大概的意思就是沒法在聲明變量 c
以前訪問變量 c
。
let
是否是挺屌的吧?!那你想不想知道 let
聲明的變量又爲啥不容許聲明提早呢?嘿嘿~ 這是由於使用 let
聲明變量的過程當中存在一個叫作暫時性死區(Temporal dead zone,簡稱 TDZ)的概念。
是否是以爲挺高深的?哈哈~ 其實沒啥高深的,大叔就給你嘮扯明白這個事兒。規矩不變,咱仍是先看段代碼再說:
if (true) { console.log(a) let a; console.log(a) a = "a"; console.log(a) }
大叔想先問問你這段代碼裏面三處打印的結果分別是啥?你得認真的尋思尋思哈~ 這可都是大叔剛和你嘮過的內容。
ReferenceError: Cannot access 'c' before initialization
對於這樣的結果,大叔估計你應該會明白,畢竟都是剛嘮過的內容。接下來,你得認真的看了,由於大叔要和你來嘮扯有關暫時性死區的概念了~
所謂的暫時性死區,就是說使用 let
關鍵字聲明的變量直到執行定義語句時纔會被初始化。也就是說,從代碼從頂部開始執行直到變量的定義語句執行,這個過程當中這個變量都是不能被訪問的,而這個過程就被叫作暫時性死區。
具體到上面這段代碼的話,實際上暫時性死區的開始和結束位置就像下面這段代碼標註的同樣嬸兒:
if (true) { // 暫時性死區開始 console.log(a); // 報錯,ReferenceError: Cannot access 'a' before initialization let a; // 暫時性死區結束 console.log(a); // 輸出undefined a = "a"; console.log(a); // 輸出a }
撈到這會兒,大叔相信你應該能夠明白啥子是暫時性死區了。其實啊,一些新的概念也沒啥難理解的,主要是你理解的角度和方式的問題。
typeof
運算符也再也不安全整體上來講,let
關鍵字要比 var
關鍵字嚴格了許多,致使咱們開發時遇到的問題相應會減小許多。但 let
就沒有任何問題了嗎?答案顯然不是滴,大叔一直信奉一句話:任何技術都沒有最優,只有最適合。
ES6 新增的 let
關鍵字也是如此,就比方說剛纔我們撈的暫時性死區的內容,其實就有問題。啥問題呢?你還記得 JS 裏面有個運算符叫作 typeof
吧,就是用來判斷原始數據類型的。這個運算符在 let
出現以前相對是比較安全的,說白了就是不容易報錯。但在 let
出現以後就不必定了,比方說若是你把它用在剛纔說的暫時性死區裏面,它就會報錯了:
if (true) { console.log(typeof c) let c; }
這段代碼最終打印的結果一樣是報錯,報錯內容一樣是:ReferenceError: Cannot access 'c' before initialization
。
關於 let
關鍵字我們撈到這會兒,其實基本上已經嘮完了。可是,但但是,可可是~ 嘿嘿~ let
還有一個最重要的特性大叔還沒和你嘮呢,這重量級的都得最後出場不是?!
那這個最重要的特性就是啥呢?叫作塊級做用域。嘮到做用域想必你應該知道在 ES5 中存在兩個:全局做用域和函數做用域,但在 ES6 中又新增了一個塊級做用域。
想嘮明白什麼是塊級做用域,咱就得從爲啥須要塊級做用域嘮起啊~ 規矩不變,仍是先看段代碼:
var a = "a" function fn() { console.log(a) if (false) { var a = "b" } } fn()
你以爲這段代碼運行以後打印的結果應該是啥?是 a?是 b?仍是... ...?其實結果是 undefined。固然了,這個結果不可貴出,你運行一下就能看到。關鍵在於,爲啥是這麼個結果?!
由於就在於 ES5 只有全局做用域和函數做用域,而上面這段代碼的結果產生的緣由就在於局部變量覆蓋了全局變量。固然了,還有比這更麻煩的問題呢,比方說我們再看下面這段代碼:
for (var i = 0; i < 5; i++) { console.log("循環內:" + i) } console.log("循環外:" + i)
是否是無比地熟悉吧?!不就是個 for
循環嘛!關鍵在哪?關鍵在於 for
循環結束以後,你會發現依舊能訪問到變量 i
。這說明啥?說明變量 i
如今是一個全局變量。固然了,你可能會說這沒啥問題,畢竟以前一直不都是這個樣子的嘛。
可是,大叔要和你說的是,如今不同了啊,如今有塊級做用域啦!啥是塊級做用域?仍是看段代碼先:
if (true) { let b = "b" } console.log(b)
這段代碼運行以後打印的結果是報錯,報錯的內容是:SyntaxError: Lexical declaration cannot appear in a single-statement context
。
這說明啥?這就說明如今你使用 let
聲明的變量在全局做用域中訪問不到了,緣由就是由於使用 let
聲明的變量具備塊級做用域。
接下來你的問題可能就是這個塊級做用域在哪呢吧?其實這個塊級做用域就是在花括號({}
)裏面。比方說,我們如今把上面那個 for
循環的代碼用 let
改造一下再看看:
for (let i = 0; i < 5; i++) { console.log("循環內:" + i) } console.log("循環外:" + i)
改造完的這段代碼運行以後的結果就是在循環結束後的打印結果是報錯,報錯內容大叔就不說了,由於都一個樣。
整明白了啥是塊級做用域,接下來大叔就得和你嘮叨嘮叨須要注意的事兒了。就是在使用 let
關鍵字聲明塊級做用域的變量時可必須在這對 {}
裏面啊,否則一樣也會報錯滴。
比方說,我們常常在使用 if
語句時愛把 {}
省略,可是若是 if
語句裏面是使用 let
聲明變量的話就不行了。不信來看段代碼吧:
if (true) let c = 'c'
這段代碼的運行結果一樣是報錯,並且報錯內容都是同樣的。但是不能忘記哦~
好了,整明白啥是塊級做用域了,也嘮清楚須要注意的了,你是否是想問問這塊級做用域有啥子用處啊?大叔都想你內心面去了,嘿嘿~
你知道匿名自調函數吧?還記得怎麼寫一個匿名自調函數嗎?是否是這樣嬸兒的:
(function(){ var msg = 'this is IIFE.' console.log(msg) })()
還記得匿名自調函數的做用不?是否是就是爲了定義的變量和函數不污染全局命名空間?!有了 let
,有了塊級做用域,上面這段匿名自調函數就能夠寫成這樣嬸兒的:
{ let msg = 'this is IIFE.' console.log(msg) }
簡化了很多吧?!
好了,整到這兒,ES6 新增的 let
關鍵字全部大叔想和你嘮扯的內容都嘮扯完了,也但願能對你有所幫助。最後再說一句:我是不想成熟的大叔,爲前端學習再也不枯燥、困難和迷茫而努力。你以爲這樣學習前端技術有趣嗎?有什麼感覺、想法,和好的建議能夠在下面給大叔留言哦~