JavaScript 中的 var、let 和 const 的区别
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
在这篇文章中,你将学习在 JavaScript (ES6) 中创建变量的两种新方法,即 `var`let和const`var`。我们将探讨 ` varvar`、 `var`let和`var` 之间的区别const,并涵盖函数作用域与块作用域、变量提升和不可变性等主题。
ES2015(或 ES6)引入了两种新的变量创建方式:`__var__`let和const`__var__`。但在深入探讨 `__var__` var、let`__var__` 和 `__var__`之间的区别之前const,你需要先了解一些基础知识。这些基础知识包括变量声明与初始化、作用域(特别是函数作用域)以及变量提升。
变量声明与初始化
变量声明引入了一个新的标识符。
var declaration
上面我们创建了一个名为 declaration 的新标识符。在 JavaScript 中,变量在创建时会被初始化为当前值undefined。这意味着如果我们尝试记录该declaration变量的值,我们会得到undefined。
var declaration
console.log(declaration) // undefined
所以如果我们记录声明变量的值,我们会得到 undefined。
与变量声明相反,变量初始化是指第一次给变量赋值。
var declaration
console.log(declaration) // undefined
declaration = 'This is an initialization'
所以这里我们declaration通过将变量赋值为一个字符串来初始化它。
这就引出了我们的第二个概念:范围。
范围
作用域定义了程序中变量和函数的可访问范围。在 JavaScript 中,有两种作用域:全局作用域和函数作用域。根据官方规范,
“如果变量语句出现在函数声明中,则变量在该函数中具有函数局部作用域。”
这意味着,如果你用var`this` 创建一个变量,那么该变量的作用域仅限于它被创建的函数,并且只能在该函数内部或任何嵌套函数内部访问。
function getDate () {
var date = new Date()
return date
}
getDate()
console.log(date) // ❌ Reference Error
上面我们试图在声明该变量的函数外部访问它。因为date该变量的作用域限定在getData函数内部,所以它只能在函数内部getDate或其嵌套函数内部访问getDate(如下所示)。
function getDate () {
var date = new Date()
function formatDate () {
return date.toDateString().slice(4) // ✅
}
return formatDate()
}
getDate()
console.log(date) // ❌ Reference Error
现在我们来看一个更高级的例子。假设我们有一个数组prices,我们需要一个函数,该函数接收这个数组以及一个discount折扣后的价格,并返回一个新的折扣后价格数组。最终目标可能如下所示。
discountPrices([100, 200, 300], .5) // [50, 100, 150]
实现方式可能如下所示
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,可能不会觉得这有什么问题。但是,如果你是从其他编程语言(特别是作用域封闭的编程语言)转过来的,你可能会对这里发生的情况感到有些疑惑。这其实并没有什么问题,只是有点奇怪。在循环之外,我们其实没有理由继续访问 `$('______') i`、` discountedPrice$('______')` 和 `$('______')` 。这样做对我们没有任何好处,在某些情况下甚至可能有害。然而,由于使用 `$('______')` 声明的变量是函数作用域的,所以你确实可以访问它们。finalPriceforvar
let现在我们已经讨论了变量声明、初始化和作用域,在深入探讨之前,我们需要澄清的最后一件事const是变量提升。
吊装
还记得我们之前说过“在 JavaScript 中,变量在创建时会被初始化为默认值undefined”吗?实际上,“变量提升”就是这么回事。JavaScript 解释器会undefined在所谓的“创建”阶段为变量声明赋予一个默认值。
有关创建阶段、提升和作用域的更深入指南,请参阅
“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 VS let VS const
首先,我们来比较一下 `$ var` 和 `$ `。它们let的主要区别在于,`$` 的作用域是函数级的,而` $` 的作用域是代码块级的。这意味着用 `$` 关键字创建的变量在其创建的“代码块”内以及任何嵌套的代码块中都可用。我所说的“代码块”指的是任何被花括号括起来的内容,例如循环或语句中的代码块。varletletlet{}forif
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
}
记住,我们之所以能够在循环外记录 `log` i、discountedPrice`log` 和 `log`,是因为它们使用 `is` 声明且作用域为函数。但是现在,如果我们把这些声明改成使用 `is`并尝试运行它,会发生什么呢?finalPriceforvarvarvarlet
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{{} finalPrice` 或 `{{}`)会像我们刚才看到的那样,导致引用错误。
var VS let
var: function scoped
let: block scoped
下一个区别与变量提升(Hoisting)有关。我们之前说过,变量提升的定义是“JavaScript 解释器会undefined在所谓的‘创建’阶段为变量声明分配一个默认值”。我们甚至通过在变量声明之前对其进行日志记录来验证这一点(你会得到 0 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
}
我想不出任何需要在变量声明之前就访问它的情况。抛出一个 ReferenceError 似乎比返回 null 更合适undefined。事实上,这正是 ` letif` 函数的工作原理。如果你尝试访问一个用 `if` 声明的变量let,而该变量尚未声明,那么你不会undefined像使用 `if` 声明的变量那样得到 null var,而是会得到一个 ReferenceError。
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
let VS const
var既然你已经理解了 ` a` 和 ` b` 的区别let,那么 `c` 呢const?实际上,`c`const几乎与 `b` 完全相同let。然而,唯一的区别在于,一旦你使用 `c` 为变量赋值const,就不能再将其重新赋值。
let name = 'Tyler'
const handle = 'tylermcginnis'
name = 'Tyler McGinnis' // ✅
handle = '@tylermcginnis' // ❌ TypeError: Assignment to constant variable.
以上结论表明,用 `&` 声明的变量let可以重新赋值,但用 `&` 声明的变量const不能重新赋值。
很好,所以任何时候你想让一个变量不可变,都可以用 `inmutable` 声明它const。嗯,也不完全是这样。用 `inmutable` 声明的变量const并不意味着它就是不可变的,它仅仅意味着这个值不能被重新赋值。这里有一个很好的例子。
const person = {
name: 'Kim Kardashian'
}
person.name = 'Kim Kardashian West' // ✅
person = {} // ❌ Assignment to constant variable.
请注意,更改对象的属性并不会重新赋值,因此即使对象是用 `@Change` 声明的const,也不意味着您不能修改它的任何属性。这仅仅意味着您不能将其重新赋值为新值。
现在,我们尚未解答的最重要问题是:应该使用 `int` var、let`int` 还是 `int` const?最普遍的观点,也是我认同的观点,是const除非你知道变量会改变,否则应该始终使用 `int`。原因在于const,使用 `int` 会向未来的自己以及任何将来需要阅读你代码的开发者发出信号:这个变量不应该改变。如果它需要改变(例如在for循环中),则应该使用 `int` let。
因此,在会变化的变量和不会变化的变量之间,剩下的变量并不多。这意味着你以后都不需要再用到它了var。
现在,虽然这种观点并不受欢迎,但它仍然有一定的道理,那就是你永远不应该使用 `int`,const因为即使你试图表明变量是不可变的,正如我们上面看到的,事实并非完全如此。持这种观点的开发者总是使用 `int`,let除非他们有像 `int` 这样的实际常量变量_LOCATION_ = ...。
总结一下,`$ var` 是函数作用域的,如果你尝试使用var在实际声明之前用 `$` 声明的变量,你会得到一个错误undefined。` const$`let是阻塞作用域的,如果你尝试使用在声明之前let或之后用 `$` 声明的变量,你会得到一个引用错误。最后,`$`和const`$` 的区别在于,一旦你给 `$` 赋值,你就不能再给它赋值,但对于 `$` ,你可以重新赋值。letconstconstlet
var VS let VS const
var:
function scoped
undefined when accessing a variable before it's declared
let:
block scoped
ReferenceError when accessing a variable before it's declared
const:
block scoped
ReferenceError when accessing a variable before it's declared
can't be reassigned
本文最初发表于TylerMcGinnis.com,是其Modern JavaScript课程的一部分。
文章来源:https://dev.to/tylermcginnis/var-vs-let-vs-const-in-javascript-4a1f