- 原文連接:var vs let vs const in javascript
- 原文做者:Tyler McGinnis
- 譯者:Alisa
在這篇文章中,你將學習兩種在JavaScript(ES6)中建立變量的新方法,let和const。 在此過程當中,咱們將研究var,let和const之間的差別,以及函數與塊級做用域,變量提高和不變性等主題。javascript
若是你更喜歡觀看視頻的方式,能夠看這個:java
var vs let vs const: Variable declarations in ES6 | ES2015編程
ES2015(或ES6)引入了兩種建立變量的新方法,let和const。但在咱們真正深刻研究var,let和const之間的差別以前,首先須要瞭解一些先決條件。 它們是變量聲明與初始化,做用域(特別是函數做用域)和提高。數組
變量聲明引入了新的標識符。bash
var declaration
複製代碼
上面咱們建立了一個名爲declaration的新的標識符。在JavaScript中,變量在建立時使用undefined
值初始化。這意味着若是咱們嘗試打印declaration
變量,咱們將獲得undefineed。編程語言
var declaration
console.log(declaration)
複製代碼
因此若是咱們打印declaration變量,獲得undefined.ide
與變量聲明相反,變量初始化是指首次爲變量賦值。函數
var declaration
console.log(declaration) // undefined
declaration = 'This is an initialization'
複製代碼
因此這裏咱們經過將變量賦值爲一個字符串來初始化它。學習
這引出了咱們的第二個概念,做用域。ui
做用域定義了在你的程序中能夠訪問變量和函數的位置。在JavaScript中,有兩種做用域 - 全局做用域和函數做用域。根據官方規範,
若是變量語句出如今函數聲明中,那麼這個變量在該函數中做爲函數局部做用域被定義。
這就意味着若是你使用var
建立了一個變量, 那麼該變量的做用域爲它被建立時所在的函數,而且只能在該函數或任何嵌套函數內部訪問。
function getDate () {
var date = new Date()
return date
}
getDate()
console.log(date) // ❌ Reference Error
複製代碼
如今讓咱們看一個更高級的例子。假設咱們有一個數組prices
,而且咱們須要一個函數,接受該數組以及一個discount
做爲參數,並返回一個新的折扣後的價格數組。最終目標可能以下所示:
discountPrices([100, 200, 300], .5)
複製代碼
而且實現可能像這樣:
function discountPrices (prices, discount) {
var discounted = []
for (var i = 0; i < prices.length; i++) {
var discountedPrice = prices[i] * (1 - discount)
var finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
return discounted
}
複製代碼
看起來很簡單,但這與做用域有什麼關係?看一下for
循環。在其內部聲明的變量是否能夠在其外部訪問?事實證實,他們能夠。
function discountPrices (prices, discount) {
var discounted = []
for (var i = 0; i < prices.length; i++) {
var discountedPrice = prices[i] * (1 - discount)
var finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
複製代碼
若是JavaScript是你知道的惟一的編程語言,你可能不會想到這一點。可是,若是你從另外一種編程語言(特別是一種塊級做用域的編程語言)開始使用JavaScript,那麼你可能會對這裏發生的事情感到擔心。
它並無真正破碎,它只是有點奇怪。在for
循環以外,沒有理由仍然能夠訪問i
,discountedPrice
和finalPrice
。它對咱們沒有任何好處,甚至可能在某些狀況下對咱們形成傷害。可是,因爲用var
聲明的變量是函數做用域內的,因此你能夠這樣作。
如今咱們討論了變量聲明,初始化和做用域, 那麼在咱們深刻了解let
和const
以前咱們須要清除的最後一件事就是變量提高。
記得早些時候咱們說過「在JavaScript中,變量在建立時用undefined
值來初始化。」事實證實,這就是變量提高的所有內容。JavaScript解釋器將在所謂的「建立」階段爲變量聲明分配默認值undefined
。
有關建立階段,變量提高和範圍的更深刻的指南,請參閱The Ultimate Guide to Hoisting, Scopes, and Closures in JavaScript
咱們來看看前面的例子而且研究一下變量提高是怎麼影響它的。
function discountPrices (prices, discount) {
var discounted = undefined
var i = undefined
var discountedPrice = undefined
var finalPrice = undefined
discounted = []
for (var i = 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
return discounted
}
複製代碼
請注意,全部變量聲明都分配了默認值undefined
。這就是爲何若是你在實際聲明以前嘗試訪問其中一個變量,你就會獲得undefined
。
function discountPrices (prices, discount) {
console.log(discounted) // undefined
var discounted = []
for (var i = 0; i < prices.length; i++) {
var discountedPrice = prices[i] * (1 - discount)
var finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
複製代碼
如今你已經瞭解了有關var
的全部內容,讓咱們最後談談你真正關心的話題:var
,let
和const
之間的區別是什麼?
首先,咱們比較一下var
和let
。 var
和let
的主要區別在於,let
做用域是塊級的,而不是函數級別的。這意味着使用let
關鍵字建立的變量在建立它的「塊」內以及任何嵌套塊中均可用。當我說「塊」時,我指的是在for
循環或if
語句中用花括號{}
包圍的任何東西。
讓咱們最後一次回顧咱們的discountPrices
函數。
function discountPrices (prices, discount) {
var discounted = []
for (var i = 0; i < prices.length; i++) {
var discountedPrice = prices[i] * (1 - discount)
var finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
複製代碼
記住,咱們可以在for
循環以外打印i
,discountedPrice
和finalPrice
,由於它們是用var
聲明的,而var
是做用域是在函數內。可是如今,若是咱們將var
聲明更改成使用let
,並嘗試運行,會發生什麼?
function discountPrices (prices, discount) {
let discounted = []
for (let i = 0; i < prices.length; i++) {
let discountedPrice = prices[i] * (1 - discount)
let finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
discountPrices([100, 200, 300], .5) // ❌ ReferenceError: i is not defined
複製代碼
:no_good: 咱們獲得ReferenceError:i is not defined
。這告訴咱們的是,使用let
聲明的變量是做用於塊級做用域的,而不是函數做用域。所以,嘗試訪問咱們聲明的「塊」以外的i
(或discountedPrice
或finalPrice
)會給咱們一個引用錯誤,就像咱們剛剛看到的那樣。
var VS let
var: 做用域是函數範圍的
let: 做用域是塊級範圍的
複製代碼
下一個區別與變量提高有關。以前咱們說過,變量提高的定義是「JavaScript解釋器會在所謂的'建立'階段將變量賦值爲undefined
這個默認值。」咱們甚至在變量聲明以前經過打印這個變量看到了這一點(你獲得了undefined
)。
function discountPrices (prices, discount) {
console.log(discounted) // undefined
var discounted = []
for (var i = 0; i < prices.length; i++) {
var discountedPrice = prices[i] * (1 - discount)
var finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
複製代碼
我想不出任何你會想要在聲明變量以前訪問它的用例。看起來拋出一個引用錯誤比返回undefined
更好。
實際上,這就是let
所作的。若是你試圖在被聲明以前去訪問一個用let
聲明的變量,你會獲得一個引用錯誤,而不是undefined
。(好比使用var聲明的那些變量)
function discountPrices (prices, discount) {
console.log(discounted) // ❌ ReferenceError
let discounted = []
for (let i = 0; i < prices.length; i++) {
let discountedPrice = prices[i] * (1 - discount)
let finalPrice = Math.round(discountedPrice * 100) / 100
discounted.push(finalPrice)
}
console.log(i) // 3
console.log(discountedPrice) // 150
console.log(finalPrice) // 150
return discounted
}
複製代碼
var 與 let
var:
函數範圍的
在變量聲明以前使用時獲得undefined
let:
做用範圍是塊級的
在變量聲明以前使用時報引用錯誤
複製代碼
既然你理解了var
和let
之間的區別,那麼const
呢?事實證實,const
與let
幾乎徹底相同。可是,惟一的區別是,一旦使用const
爲變量賦值,就沒法將其從新賦值給新值。
let name = 'Tyler'
const handle = 'tylermcginnis'
name = 'Tyler McGinnis' // ✅
handle = '@tylermcginnis' // ❌ TypeError: Assignment to constant variable.
複製代碼
上面的內容是用let
聲明的變量能夠從新賦值,但用const
聲明的變量不能。
很酷,因此只要你想讓一個變量是不可變的,你能夠用const聲明它。嗯,也不徹底是這樣。由於用const
聲明變量並不意味着它是不可變的。它意味着不能從新賦值。這是一個很好的例子。
const person = {
name: 'Kim Kardashian'
}
person.name = 'Kim Kardashian West' // ✅
person = {} // ❌ Assignment to constant variable.
複製代碼
請注意,更改對象上的屬性並非從新賦值,所以即便使用const
聲明對象,也不意味着你不能改變其任何屬性。它只表示您沒法將其從新分配給新值。
如今咱們尚未回答的最重要的問題是:你應該使用var
,let
仍是const
? 最流行的觀點和我贊同的觀點是,除非你知道變量會發生變化,不然你應該老是使用const
。 這樣作的緣由是使用const
,你向將來的本身以及任何其餘將來的開發人員發出信號,這些開發人員必須閱讀你的代碼,這個變量不該該改變。若是它須要更改(好比在for
循環中),你應該使用let
。
所以,在變化的變量和不變的變量之間,沒有多少剩餘。這意味着你不該該再使用var
。
如今不受歡迎的觀點,雖然它仍然有一些有效性,是你永遠不該該使用const
,由於即便你試圖代表變量是不可變的,正如咱們上面所看到的那樣,狀況並不是徹底如此。同意此意見的開發人員老是使用let
,除非他們的變量其實是常量,如_LOCATION_ = ...
.
因此回顧一下,var
是做用於函數範圍的,若是你嘗試在實際聲明以前使用一個用var
聲明的變量,你就會獲得undefined
。const
和let
是做用於塊級範圍的,若是你嘗試在聲明以前使用let
或const
聲明的變量,你會獲得一個引用錯誤。最後,let
和const
之間的區別在於,一旦你爲const
賦值,你就不能從新爲它賦值,可是你可使用let
來作到從新賦值。
var 與 let 與 const
var:
函數範圍的
在變量聲明以前使用時會獲得undefined
let:
做用範圍是塊級的
在變量聲明以前使用時會報引用錯誤
const:
做用範圍是塊級的
在變量聲明以前使用時會報引用錯誤
不能從新賦值
複製代碼
這篇文章最初發佈於tylermcginnis.com,是其 Modern JavaScript課程的一部分。