[译] 在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课程的一部分。

相关文章
相关标签/搜索