[JavaScript] 你應該使用 let 而不是 var 的 3 個重要理由

realdennis
Jul 1, 2018

--

本篇文將著重於:var的作用域比let還要大,然而「蛋越大跨出步伐越容易扯到蛋」,當你使用var來宣告變數時,會遇到哪些操蛋的事,以及使用let後如何解決。

根據之前所提到的《 JavaScript中 var與let的差別》,我們可以明白varlet關鍵性的區別在於作用域(scope)的不同,而區塊作用域則是比函數作用域較小、較為精確。

在撰寫腳本的日常中,let可以幾乎完美替代var,甚至更適合剛接觸這個語言的人使用。

如果你擔心ES6的語法會讓舊的瀏覽器報錯,這時候你可以透過Babel將腳本轉為ES5的語法

“Person's arm with bracelet displaying hitch hiking sign near asphalt road” by rawpixel on Unsplash

第一件操蛋的事

for-loop處理回調函數(callback function)時會烙賽,也就是說出現預期外的結果。

如果你是從C-like語言轉過來的人,一定對for loop宣告不陌生,觀看以下程式碼:

你希望的應該是依序印出0,1,2沒錯吧?

setTimeout是一個會去註冊一個callback function,1秒後執行。在作用域沒有管控好的狀況下,loop裡頭的異步事件會去註冊起來,且使用var宣告的變數仍然會直到 i = 3 時才停止(然而這只是幾個瞬間的事)

並在一秒之後,分別被印出這些操蛋的東西。

這不是我要的for迴圈!這不是我要的for迴圈!這不是我要的for迴圈!

Photo by Thought Catalog on Unsplash

讓我們來看看古人怎麼解決這問題吧。

在冷兵器的時代,人類只能透過弓箭飛鏢來做到遠距攻擊,然而槍砲彈藥被發明後,戰爭的方式被改變了!

在ES5之前,我們沒有let這個好用的宣告方式,所以只能透過閉包(closure)來生成一個各自獨立的函數作用域,不囉唆直接看code吧。

把該死的參數傳進去閉包,並在各自的閉包裡註冊callback。

運用函數的閉包與立即執行函數(IIFE),你可以建立各自的函數作用域,並把變數i傳進去,用這種方式去解決目前遇到的問題,非常棒。

但放到現在,仍然不夠好,Programmer總希望自己的寫的腳本過一陣子,甚至是不熟悉這個語言的人也能夠看懂。

ES6(槍砲彈藥的出現)

完美也優美的解決這個問題。

第2件操蛋的事

寫起來很爽,維護起來卻很烙賽的變量提升(hoisting)。

我敢保證變量提升絕對是會讓許多本科生初接觸時說出:「X,這語言是垃圾是不是?」的一大原因。

“Close-up of colorful lines of code on a computer screen” by Markus Spiske on Unsplash

其實我並不清楚變量提升這個概念是源自於哪個語言,如果有知道的人可以幫我補充,也不太了解其中的哲學。曾有朋友戲稱:變量提升是為了降低Javascript編寫難度,最後在宣告變數作為補充說明,降低門檻。

變量提升是一個值得特別寫一篇來解釋的名詞。

觀察以下代碼,回答執行結果:

(A). ReferenceError / (B).印出20 / (C).印出undefined

答案是(C)。

在scripting language來說,發生這種事的確是滿意外的。

(1) 若是逐行執行,理所當然該印出Reference Error

(2) 若是這個區塊在function裡被loop兩次,第二次印出來的結果則是20,因為函數作用域中的變數x已被賦值。

可以把上述代碼,看成這樣:

這就是~我要的hoisting

我認為這是一個JSer必須理解的事,我們了解這些坑後,才能知道ES6/ES7的發展是為了什麼。

回到現代,看看以下代碼:

let y 會把這個區塊作用域的y變數給綁定,在宣告之前的存取皆會引用錯誤。(A.k.a 暫時性死區)

不過其實這樣子的行為比較合乎直覺。

第3件很操蛋的事

認為hoisting幫助coding速度的人,如果在最後忘記去做var宣告…

若是直接對一沒宣告過的變數做賦值的動作,可能會發生「全域作用域污染」。

這非常嚴重

“A big and well-constructed facility in the middle of the field in a foggy environment in Katerini” by Jason Blackeye on Unsplash

這個在function內的function內的g變數(有點饒舌),在沒有宣告就賦值,會直接被拉到最外頭的global上(根據宿主環境分別是global & window),如果這是一個寫給他人使用的library,絕對會讓使用者幹到飛天。

本文就到此結束,如果喜歡的話給我一些鼓掌,反之,若覺得有哪裡說的不妥歡迎留言指正及討論。

--

--