原文javascript
medium.freecodecamp.org/var-vs-let-…java
在這篇文章中,你將會在javascript(ES6)中學習兩種新的方式建立變量,let和const。在此過程當中,咱們將看到var,let和const的不一樣之處,以及包括諸如函數與塊做用域、變量提高和不變性等主題編程
ES2015(或者ES6)介紹了兩種新的方式建立變量,let和const。可是在咱們真正區分var,let和const不一樣點以前,你應該先了解一些前提條件。它們是變量聲明vs初始化,做用域(特別是函數做用域),和變量提高數組
Variable Declaration vs Initializationbash
變量聲明vs初始化編程語言
一個變量聲明介紹了一個新的標識符ide
var declaration複製代碼
上面咱們建立了一個新的標識符叫作聲明。在javascript中,當變量被建立,它們以undefined的值被初始化。這意味着,若是咱們嘗試打印聲明的變量,咱們將獲得undefined函數
var declaration
console.log(declaration)複製代碼
因此若是咱們打印聲明的變量,咱們獲得undefined學習
與變量聲明相反,變量初始化是指當你首次給一個變量賦值ui
var declaration
console.log(declaration) // undefined
declaration = 'This is an initialization'複製代碼
因此在這裏咱們經過賦值給已經聲明當變量一個字符串來初始化它。
這就引導咱們第二個概念了,做用域
做用域定義了在你的程序裏,變量和函數在哪裏能夠獲取到。在JavaScript中,有兩種類型的做用域--全局做用域和函數做用域,根據官方的規範,
若是變量語句發生在函數聲明中,則變量在這個函數中經過函數局部做用域來定義
這就意味着若是你使用var來建立一個函數,那麼這個變量被運用於它所建立的函數,而且只能在該函數內部或者任何嵌套函數內部被訪問。
function getDate () {
var date = new Date()
return date
}
getDate()
console.log(date) // ❌ Reference Error複製代碼
上面咱們嘗試獲取一個被聲明的函數以外的變量。由於date是做用域於getDate的函數,它只能被getDate它自己或者getDate中的任何嵌套函數所獲取(以下所示)
function getDate(){
var date = new Date()
function formatDate(){
return date.toDateString().slice(4) //可獲取
}
return formatDate()
}
getDate()
console.log(date) // Reference Error 引用錯誤複製代碼
如今讓咱們來看一個更高級的例子。假設咱們有一個prices的數組,而且咱們須要一個函數,這個函數接收一個像折扣的數組,而且返回一個折扣後的新數組。最後的目標可能看起來像這樣:
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以前咱們須要清除的最後一件事就是提高。
Hoisting提高
記得早的時候咱們說過,‘在JavaScript中,變量被建立時能夠用undefined值來初始化。事實證實,這就是「Hoisting」的所有。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複製代碼
咱們獲得ReferenceError: i is not defined
.這告訴咱們的是,用let聲明變量是塊做用域而不是函數做用域。因此嘗試在它們聲明的「塊」以外訪問i(或者discountedPrice or finalPrice)將給咱們一個引用錯誤,正如咱們剛纔所看到的。
var VS let
var: function scoped
let: block scoped複製代碼
下一個區別與提高相關。以前咱們說過,提高的定義是「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 VS let
var:
function scoped
undefined when accessing a variable before it's declared let: block scoped ReferenceError when accessing a variable before it's declared複製代碼
如今你理解了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
總結
var是函數做用域,而且若是你嘗試在實際聲明以前使用一個var聲明的變量,你將獲得undefined。
const和let是塊做用域,而且若是你嘗試在實際聲明以前使用let或const聲明的變量,你將獲得一個引用錯誤。
最後在let和const之間的區別是一旦你用const分配了一個值,你將不行從新賦值,可是用let能夠