[譯] 在JavaScript中什麼時候使用var、let及const

在這篇文章中,你將學習兩種在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循環以外,沒有理由仍然能夠訪問idiscountedPricefinalPrice。它對咱們沒有任何好處,甚至可能在某些狀況下對咱們形成傷害。可是,因爲用var聲明的變量是函數做用域內的,因此你能夠這樣作。

如今咱們討論了變量聲明,初始化和做用域, 那麼在咱們深刻了解letconst以前咱們須要清除的最後一件事就是變量提高。

變量提高

記得早些時候咱們說過「在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的全部內容,讓咱們最後談談你真正關心的話題:varletconst之間的區別是什麼?

var 與 let 與 const

首先,咱們比較一下varletvarlet的主要區別在於,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循環以外打印idiscountedPricefinalPrice,由於它們是用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(或discountedPricefinalPrice)會給咱們一個引用錯誤,就像咱們剛剛看到的那樣。

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:
  做用範圍是塊級的
  在變量聲明以前使用時報引用錯誤
 
複製代碼

let與const

既然你理解了varlet之間的區別,那麼const呢?事實證實,constlet幾乎徹底相同。可是,惟一的區別是,一旦使用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聲明對象,也不意味着你不能改變其任何屬性。它只表示您沒法將其從新分配給新值。

如今咱們尚未回答的最重要的問題是:你應該使用varlet仍是const? 最流行的觀點和我贊同的觀點是,除非你知道變量會發生變化,不然你應該老是使用const。 這樣作的緣由是使用const,你向將來的本身以及任何其餘將來的開發人員發出信號,這些開發人員必須閱讀你的代碼,這個變量不該該改變。若是它須要更改(好比在for循環中),你應該使用let

所以,在變化的變量和不變的變量之間,沒有多少剩餘。這意味着你不該該再使用var

如今不受歡迎的觀點,雖然它仍然有一些有效性,是你永遠不該該使用const,由於即便你試圖代表變量是不可變的,正如咱們上面所看到的那樣,狀況並不是徹底如此。同意此意見的開發人員老是使用let,除非他們的變量其實是常量,如_LOCATION_ = ....

因此回顧一下,var是做用於函數範圍的,若是你嘗試在實際聲明以前使用一個用var聲明的變量,你就會獲得undefinedconstlet是做用於塊級範圍的,若是你嘗試在聲明以前使用letconst聲明的變量,你會獲得一個引用錯誤。最後,letconst之間的區別在於,一旦你爲const賦值,你就不能從新爲它賦值,可是你可使用let來作到從新賦值。

var 與 let 與 const

var: 
  函數範圍的
  在變量聲明以前使用時會獲得undefined
  
let: 
  做用範圍是塊級的
  在變量聲明以前使用時會報引用錯誤
  
const:
  做用範圍是塊級的
  在變量聲明以前使用時會報引用錯誤
  不能從新賦值
複製代碼

這篇文章最初發佈於tylermcginnis.com,是其 Modern JavaScript課程的一部分。

相關文章
相關標籤/搜索