var、let 和 const 應該怎麼用?
在這篇文章中,我們將介紹兩種新的在 JavaScript(ES6)中建立變數的方式,即使用 let 和 const。同時,我們將探討 var、let 和 const 之間的區別,以及函式與塊作用域、變數提升和不變性等內容。
如果你想觀看視訊,這是連結: https://youtu.be/6vBYfLCE9-Q
ES2015(或 ES6)引入了兩種建立變數的新方法:let 和 const。但在我們深入瞭解 var、let 和 const 之間的區別之前,需要先了解一些先決條件。它們是變數宣告與初始化、作用域(特別是函式作用域)和變數提升。
變數宣告與初始化
變數宣告引入了新識別符號。
複製程式碼
vardeclaration
我們建立了一個叫作 declaration 的新識別符號。在 JavaScript 中,剛建立的變數會被初始化為 undefined。如果我們在控制檯列印變數 declaration,將會看到輸出 undefined。
複製程式碼
vardeclaration console.log(declaration)
與變數宣告相反,變數初始化是指首次為變數賦值。
複製程式碼
vardeclaration console.log(declaration)// undefined declaration ='This is an initialization'
我將一個字串賦值給 declaration 變數,以此來初始化它。
這引出了我們的第二個概念——作用域。
作用域
作用域定義了在程式內部可以訪問哪裡的變數和函式。JavaScript 中有兩種作用域——全域性作用域和函式作用域。官方規範中提到:
“如果變數語句出現在函式宣告中,那麼變數的作用域就是函式的區域性作用域。”
也就是說,如果你使用 var 建立一個變數,那麼該變數被“限定”在建立這個變數的函式中,並且只能在該函式或其他巢狀函式內部訪問它。
複製程式碼
functiongetDate(){ vardate=newDate() returndate } getDate() console.log(date)// :x: Reference Error
在上面的程式碼中,我們嘗試在函式外部訪問一個在函式內部宣告的變數。因為 date 作用域被限定在 getDate 函式內部,所以它只能在 getDate 內部或 getDate 中的巢狀函式中訪問(如下所示)。
複製程式碼
functiongetDate(){ vardate=newDate() functionformatDate(){ returndate.toDateString().slice(4)// :white_check_mark: } returnformatDate() } getDate() console.log(date)// :x: Reference Error
現在,讓我們來看一個更高階的例子。假設我們有一個 prices 陣列,我們需要一個函式,將這個陣列和 discount 作為引數,並返回一個新的折扣價陣列。最終的函式可能如下所示:
複製程式碼
discountPrices([100,200,300],.5)
函式的實現可能看起來像這樣:
複製程式碼
functiondiscountPrices(prices, discount){ var discounted = [] for(vari=0;i< prices.length;i++) { var discountedPrice = prices[i] * (1- discount) var finalPrice = Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } returndiscounted }
看起來很簡單,但這與塊作用域有什麼關係?看一下 for 迴圈,在其中宣告的變數是否可以在外部訪問?事實證明,這樣是可以的。
複製程式碼
functiondiscountPrices(prices, discount){ vardiscounted = [] for(vari =0; i < prices.length; i++) { vardiscountedPrice = prices[i] * (1- discount) varfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
如果 JavaScript 是你所知道的唯一程式語言,那麼你可能不會多想什麼。但是,如果你從其他程式語言(特別是具有塊作用域的程式語言)轉到 JavaScript,那麼你可能會對這個問題感到奇怪。
你可能感到很奇怪,因為我們沒有理由在 for 迴圈之外還能繼續訪問 i、discountedPrice 和 finalPrice。它對我們沒有任何好處,甚至在某些情況下可能會對我們造成傷害。但因為用 var 宣告的變數的作用域是整個函式,所以你可以這樣做。
現在,我們已經討論完變數宣告、初始化和作用域,在深入瞭解 let 和 const 之前,我們需要了解的最後一個東西是變數提升。
變數提升
之前我們說過,“在 JavaScript 中,剛建立的變數會被初始化為 undefined”。事實證明,這就是“變數提升”。JavaScript 直譯器將在所謂的“建立”階段為宣告的變數分配預設值 undefined。
有關變數建立、提升和作用域的更多內容,請參閱: https://tylermcginnis.com/ultimate-guide-to-execution-contexts-hoisting-scopes-and-closures-in-javascript/ 。
讓我們繼續前面的例子,看看變數提升是如何影響它的。
複製程式碼
functiondiscountPrices(prices, discount){ vardiscounted =undefined vari =undefined vardiscountedPrice =undefined varfinalPrice =undefined discounted = [] for(vari =0; i < prices.length; i++) { discountedPrice = prices[i] * (1- discount) finalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
請注意,所有宣告的變數都被分配了預設值 undefined。這就是為什麼如果你在實際宣告之前嘗試訪問其中的一個變數,就會得到 undefined。
複製程式碼
functiondiscountPrices(prices, discount){ console.log(discounted)// undefined vardiscounted = [] for(vari =0; i < prices.length; i++) { vardiscountedPrice = prices[i] * (1- discount) varfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
接下來讓我們來討論我們的重點:var、let 和 const 之間的區別。
var、let 和 const
首先,我們先來比較 var 和 let。var 和 let 之間的主要區別在於,let 不是函式作用域的,而是塊作用域的。這意味著使用 let 關鍵字建立的變數可以在建立它的“塊”內以及巢狀塊內訪問。這裡所說的“塊”是指用大括號{}包圍的任何東西,比如 for 迴圈或 if 語句。
讓我們再回顧一下我們的 discountPrices 函式。
複製程式碼
functiondiscountPrices(prices, discount){ vardiscounted = [] for(vari =0; i < prices.length; i++) { vardiscountedPrice = prices[i] * (1- discount) varfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
還記得嗎,我們可以在 for 迴圈之外訪問 i、discountedPrice 和 finalPrice,因為它們是用 var 宣告的,而 var 是函式作用域的。但是現在,如果我們將 var 宣告更改為 let 並執行它,會發生什麼?
複製程式碼
functiondiscountPrices(prices, discount){ letdiscounted = [] for(leti =0; i < prices.length; i++) { letdiscountedPrice = prices[i] * (1- discount) letfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted } discountPrices([100,200,300],.5)// :x: ReferenceError: i is not defined
我們會得到 ReferenceError: i is not defined 錯誤。這告訴我們,使用 let 宣告的變數是塊作用域的,而不是函式作用域。因此,試圖在“塊”之外訪問 i(或者 discountedPrice 和 finalPrice)將會得到一個很少見的錯誤。
下一個區別與變數提升有關。之前我們曾說過,變數提升是指“JavaScript 直譯器會在所謂的建立階段將宣告的變數賦值為預設值 undefined”。
複製程式碼
functiondiscountPrices(prices, discount){ console.log(discounted)// undefined vardiscounted = [] for(vari =0; i < prices.length; i++) { vardiscountedPrice = prices[i] * (1- discount) varfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
我想不出任何你需要在宣告變數之前訪問變數的理由,所以,看來丟擲一個 ReferenceError 錯誤比返回 undefined 會更好。
事實上,這正是 let 所做的事情。如果你嘗試在宣告之前訪問使用 let 宣告的變數,你將得到 ReferenceError 錯誤,而不是 undefined。
複製程式碼
functiondiscountPrices(prices, discount){ console.log(discounted)// :x: ReferenceError letdiscounted = [] for(leti =0; i < prices.length; i++) { letdiscountedPrice = prices[i] * (1- discount) letfinalPrice =Math.round(discountedPrice *100) /100 discounted.push(finalPrice) } console.log(i)// 3 console.log(discountedPrice)// 150 console.log(finalPrice)// 150 returndiscounted }
let 與 const
你已經理解了 var 和 let 之間的區別,那麼 const 呢?事實證明,const 與 let 幾乎完全相同。但是,唯一的區別是,一旦使用 const 為變數賦值,就無法對其重新賦值。
複製程式碼
letname='Tyler' consthandle ='tylermcginnis' name='Tyler McGinnis'// :white_check_mark: handle ='@tylermcginnis'// :x: TypeError: Assignment to constant variable.
在上面程式碼中,用 let 宣告的變數可以重新賦值,但用 const 宣告的變數不能。
所以,如果你想要讓一個變數不可變,可以用 const 來宣告它。但其實不然,只是用 const 宣告變數並不意味著它是不可變的,只是無法對其重新賦值而已。請看下面的例子。
複製程式碼
const person = { name:'Kim Kardashian' } person.name ='Kim Kardashian West'// :white_check_mark: person = {} // :x: Assignmenttoconstantvariable.
請注意,修改物件的屬性並不會導致重新賦值,所以,即使使用 const 來宣告物件,並不意味著你不能修改它的屬性。它只是表示你無法為其重新賦值。
但我們還沒有回答最重要問題:你應該使用 var、let 還是 const?流行的觀點認為,除非你知道變數會發生變化,否則應該總是使用 const。因為這是在向未來的自己以及未來會閱讀你的程式碼的其他開發者發出訊號:不應該修改這些變數。如果它需要被修改(比如在 for 迴圈中),你應該使用 let。
因此,在會發生變化的變數和不會發生變化的變數之間,剩下的就不多了。這意味著你不應該再使用 var。
同時也存在一個不是很流行的觀點,就是永遠不應該使用 const,因為即使你試圖表明變數是不可變的,正如我們上面所看到的那樣,情況並非完全如此。接受這個意見的開發者總是使用 let,除非他們的變數是常量,例如 _LOCATION_
等。
這裡再回顧一下,var 是函式作用域的,如果你嘗試在宣告之前訪問用 var 宣告的變數,就會得到 undefined。const 和 let 是塊作用域的,如果你嘗試在宣告之前訪問用 let 或 const 宣告的變數,你會得到一個 ReferenceError 錯誤。最後,let 和 const 之間的區別在於,一旦你為用 const 宣告的變數賦值,你就不能重新賦值,但如果變數是用 let 宣告的,是可以重新賦值的。
更多內容,請關注前端之巔。
英文原文: https://medium.freecodecamp.org/var-vs-let-vs-const-in-javascript-2954ae48c037