JavaScript 中 var, let, const 的区别

swift 发布于 2023年06月08日

最近在做微信小程序相关的事情,就又拾起了 JavaScript。 对于互联网开发者来说,无论你开发何种类型的应用,或多或少都会接触到它。闲言少叙,直奔主题。

var,let 和 const

这三个都是在 JavaScript 中声明变量的关键字。 早期的互联网应用中,很多是以 Web 页面的形式进行交互,当时的 JavaScript 更多是用于前端页面相关的开发,大多数浏览器也没有对 letconst 关键字的支持。 所以 var 关键字是最早 JavaScript 开发者使用并习惯的声明变量的方法。

也是随着 ES 标准的逐步完善,以及主流浏览器逐渐对标准的支持, 更进一步,现在 JavaScript 的应用不仅限于浏览器前端,包括服务端,以及其他生态越来越多的使用 JavaScript 环境, 比如微信小程序。

这也就引出了后两个关键字 let 和 const 的出现,我们来分别看看他们各自的区别吧。

作用域

var

首先从作用域来说, var 声明的变量只支持全局作用域函数作用域。比如下面代码:

function test() {
{
var x = 1;
}
console.log(x); // 输出: 1
}

这里最终的输出内容是 1。 也就是 x 变量的值。 如果你有过其他语言开发经验,肯定会觉得,x 明明是在一对大括号的代码块中定义的,怎么可能在代码块(Block) 外面还可以访问到 x 变量,还能正常输出它的值呢?

上面代码不难看出, var 声明的变量是没有块作用域这个概念的。我们在 test 函数中定义的变量 x 在整个函数内都是可以访问到的, 无论它在多少层的代码块内声明, 只要他的定义在函数内, 那么所有地方都能访问到它。

这个机制在某些情况下,可能就会引发一些奇怪的效果,比如:

function test() {
var list = [1,2,3];
var x = 1;
for(const item of list) {
var x = item;
console.log(x); // 输出: 1,2,3
}
console.log(x); // 输出: 3
}

上面代码中, 我们先在外层定义了一个 x 变量, 然后又在 for 循环中定义了另一个 x 变量。 按照我们通常的理解, 这两个 x 不在一个作用域,互相不影响。 但结果恰恰出乎意料,for 循环内按照预期输出了 list 数组的所有内容, 但是我们外层的最后一行 console.log,输出的却是 3, 也就是 list 数组最后一个数字。

就是说 for 循环中定义的 x, 已经影响到外层的 x 了。 这是因为 var 定义的变量没有 块作用域 的概念。只有全局作用域函数作用域。 要么它在函数外面定义,对整个代码文件可访问。 要么它定义在函数中,对整个函数可访问。

let 和 const

let,constvar 最主要的区别就是支持块作用域. 我们再来看同样的代码:

function test() {
var list = [1,2,3];
let x = 1;
for(const item of list) {
let x = item;
console.log(x); // 输出: 1,2,3
}
console.log(x); // 输出: 1
}

这次两个 x 变量都用 let 来声明,从输出结果来很容易看出, 用 let 声明的变量正确的将外层的 xfor 循环里面的分离开来。

反复声明同一个变量

对于 var 声明的变量,可以重复声明,不会造成运行时错误:

var x = 10;
var x;

上述代码可以正常运行。 但是对于 let 声明的变量, 如果重复声明的话就会触发运行时错误:

let x = 10;
let x;

运行上面代码会产生错误:

SyntaxError: Identifier 'x' has already been declared
var 的 hoisting 机制

所谓 hoisting 机制,可以理解为 var 声明的变量只要代码中存在,不管顺序先后,都会先进行一次初始化。 如下代码:

console.log(x); // 输出 undefined
var x = 10;
console.log(x); // 输出 10

console.log(y); // ReferenceError: y is not defined

我们在上面代码中用 var 定义了变量 x。 第一行代码在定义变量之前就尝试输出它,输出了结果 undefined。 而最后一行代码,尝试输出一个从未在代码文件中定义的变量 y,造成了运行时错误 ReferenceError: y is not defined

可见,访问一个从未定义的变量 y, 和在它定义之前访问变量 x 的运行结果是不同的。

var x = 10; 虽然不是在代码最前面定义的, 但是 JavaScript 运行环境在加载代码文件时, 会预先将所有 var 定义的变量进行一次初始化, 然后再才开始执行其他代码。 这也就是造成我们上面代码输出的原因。

let 和 const 的 TDZ 机制

TDZTemporal dead zone 的缩写,简单来说就是我们使用 letconst 声明的变量,会在 JavaScript 加载代码的时候先存入 Temporal dead zone 的区域。 直到代码执行到 let 定义这个变量的语句才会结束:

console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 10;

执行上面代码,就会报注释中的那个错误。同样,const 方式声明的变量也会是这个机制。

let 与 const 的区别

let 关键字声明的变量,是可以通过赋值操作符 = 修改引用内容的。 而 const 不能。 从名字中也不难理解了, const 经常标识常量的意思。 虽然通过 = 修改 const 声明的变量本身。 但是如果 const 引用的是数组或是 JSON 对象,我们还是能修改其中的内容的:

const x = 10;
x = 20; //TypeError: Assignment to constant variable.

const y = [1,2,3];
y[0] = 3;
console.log(y); // [3,2,3]
写在最后

以上就是 JavaScriptvar, letconst 的主要区别了。当然为了每篇文章尽量精简,只把最重要的内容给大家做了介绍,如果对这个内容还想进一步研究。 大家可以参考 MDN 相关的文档:

var https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var

let https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

const https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const


如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe