JavaScript中var、let和const的区别

阅读前须知!

什么是 ECMAScript:

这里只做简单的描述,ECMAScript 简称 ES,完整的 JavaScript 包括 ECMAScriptBOMDOM。在基础层面,它描述了一门语言的语法、类型、关键字、操作符等部分。它不只被 JavaScript 实现了,它可以被任何语言实现,只要这个语言符合 ECMAScript 的规范描述。如果想要深入了解 ECMAScript ,推荐参考一些其他资料。

ECMAScript 中的变量:

ECMAScript 变量是松散类型的,其含义为变量可以用来保存任意类型的数据。例如,一个变量可以用来保存数字 5,也可以用来保存字符串 “Hello Yerri!”、你甚至可以用这个变量来保存一个对象,只不过这里的对象指的是对象的引用,而不是对象的本身。

var 关键字

var 声明方式可以使用在 ES 的所有版本中。

变量的声明:

var name;
// 这里使用 var 关键字声明了一个名为 name 的变量,它的初始值为:undefined

变量的初始化:

// 变量的初始化即在声明变量的同时就给它一个初始值
var name = "Hello";
// 这里的给 name 变量初始化了一个字符串类型的值: "Hello" 
name = 5;
// 这里我们修改 name 的值为数字类型:5
// 虽然 ES 允许这么做,但并不推荐改变原始的数据类型!

var 的作用域

先来看一段代码:

function test() {
    var name = "Hello"; // 在函数里定义了一个变量
    console.log(name);   
}
test();     // 调用这个函数后控制台输出为:Hello
console.log(name);  // 这里会报错!  

var 在函数内部声明的变量为局部变量,当函数执行完毕时,这个变量会被销毁,因此,最后一句代码运行时会报错。

在声明变量时如果省略 var 关键字,这个变量会成为全局变量,用下面的代码演示一下:

function test() {
    name = "Hello"; // 在函数里定义了一个变量
    console.log(name);   
}
test();     // 调用这个函数后控制台输出为:Hello
console.log(name);  // 这里也会输出为:Hello

虽然可以省略 var 来声明全局变量,但并不推荐这么做,这样会导致代码很难维护!因为你无法确定,你使用的是已经声明过的变量还是重新定义的全局变量。

声明提升

先来看一段代码:

function test() {
    console.log(name);  // 在变量声明前输出这个变量
    var name = "Hello";
}
test();

上面的代码会报错吗?不会,它可以正确的执行,并且输出结果为:Hello。

下面的代码与上面的代码是等价的:

function test() {
    var name = "Hello"; // 先声明变量
    console.log(name);  // 然后输出这个变量
}
test();

这就是 var 声明提升,var 声明的变量可以自动被提升到函数块中的顶部。

let 关键字

let 和下面要讲的 const 关键字都是在 ES6(于2015年发布) 之后出现的,其目的是解决 var 的声明方式存在的一些问题。

let 的声明和初始化与 var 一致,其最大的区别是:

  • let 的声明范围是块作用域

  • var 的声明范围是函数作用域

  • let 不可以重复定义一个变量

下面用代码来直观的展示一下区别:

function test() {
    // 我们把变量声明在 if 的代码块中
    if(true) {
        var name = "Hello";
        let age = 22;
        console.log(name);  // 输出为:Hello
        console.log(age);   // 输出为:age
    }
    console.log(name);  // 输出为:Hello
    console.log(age);   // 报错!因为变量 age 没有被定义
}

上述代码中,因为 var 声明范围是函数作用域,所以变量 name 在整个函数内可用。而 let 的声明范围是块作用域,所以它只能在 if 语句块内使用,在 if 语句块外使用时会报错。

let 与 var 的另一个区别是:let 声明的变量不会被提升!例如下面的代码执行时会报错:

function test() {
    console.log(name);  // 在变量声明前输出这个变量
    let name = "Hello";
}
test(); // 执行函数时会抛出 ReferenceError 错误!

还记得上面提到过 let 的出现是为了解决 var 的声明方式存在的一些问题吗?

我们举个例子,并用代码来实现它。将数字 1、2、3、4、5 延迟一秒后打印出来,分别用 var 和 let 来实现它:

// var 实现
for(var i=1; i<=5; i++) {
    // 创建一个定时器,一秒延迟后执行内部函数代码
    setTimeout( function() {
        console.log(i);
    }, 1000);
}

建议先将上面的代码运行一下看看输出结果如何,从理论上讲,输出的值应该为 1,2,3,4,5 才对。因为第一次循环,我们创建一个延迟一秒的定时器,并用于输出当前的 i 值,第二次到第五次也是同样的目的,所以我们共创建了五个定时器,这些定时器都会在一秒后被添加到执行队列中,之后逐个打印出 i 的值。但最终的输出结果却为:6,6,6,6,6 。为什么会发生这样的事情?这是因为 var 定义的 i 变量会渗透到 for 循环的外部,可以暂时理解为下面这种形式,这种描述方式并不准确,仅作为初学者的参考:

var i;
for(i=1; i<=5; i++) {
    // 省略定时器代码
}

计算机执行当前代码的效率是非常快的,从微观角度,这五个定时器是逐个添加的,从宏观的角度来看,它们是一起被添加的。这就是为什么五个数字一秒后会被同时输出,如果无法理解,推荐先去了解一下 Javascript 中的定时器,之后我也会专门写一篇关于定时器的文章。

由于整个 for 循环一瞬间就结束了,同时五个定时器也在一瞬间就被创建完成,并且当 i=5 的时候,i++ 会使 i 的值变为 6 。由于每个定时器的延迟时间为一秒,所以一秒之后,定时器中 console.log(i) 语句中 i 的值其实就是 6 ,如果了解作用域链和闭包,这一定非常容易理解。这样的定时器一共有五个,所以同时输出结果为:6,6,6,6,6 。

下面用 let 实现同样的问题:

// let 实现
for(let i=1; i<=5; i++) {
    // 创建一个定时器,一秒延迟后执行内部函数代码
    setTimeout( function() {
        console.log(i);
    }, 1000);
}

上述代码不会出现 var 声明方式中存在问题,而是正确的打印出 1,2,3,4,5 ,不信你可以试试!

使用 let 声明的变量 i 只能作用于当前循环的块内,而且,使用 let 方式声明变量时,JavaScript 引擎会在后台为每次循环的 i 值重新定于一个新的变量作用于 console.log(i) 语句,可以抽象的理解为这些 i 它们不是同一个 i 。因此,我们可以得到正确的结果。这就是 let 和 var 在 for 循环中的区别,也直观的反应了它们之间作用域的区别。

const 关键字

const 与 let 基本相同,但存在以下几点区别:

  • const 定义的变量必须初始化

  • 不可以修改由 const 定义的变量的值

可以理解为 const 是用来定义常量的,在使用 const 时有以下几点需要注意。

const 引用对象时对象内部的值可以修改

如果 const 变量引用的是一个对象,那么修改对象内部的属性不会违反 const 不可修改值的原则,可以理解为 const 只关心引用的对象位置,对象内部的值 const 不关心,可以随便修改。如下面的代码示例:

// 定义两个对象:
const obj1 = {name:"Hello", age:23};
const obj2 = {name:"world", age:22};
// 改变 obj1 里面的值
obj1.age = 22;
console.log(obj1);  // 这里 age 的值会被改为:22
// 尝试把 obj2 的引用给 obj1
obj1 = obj2;
console.log(obj1);  // 报错:TypeError: Assignment to constant variable.

报错原因是我们尝试修改 obj1 的引用位置,这对于 const 来说是不允许的!

const 不可以用来声明迭代变量:

for(let i=1; i<=5; i++) {} 的使用方式是正确的,for(const i=1; i<=5; i++) {} 的使用方式是错误的,因为在迭代的过程中,i 的值会发生改变,这违反了 const 值不可修改的原则。

但 const 也不是不可以用在 for 循环中,比如下面介绍的两个例子:

let i = 0;
for(const j=5; i<j; i++) {
    // 仔细观察代码,因为 j 的值不发生改变,所里这个块里面的语句可以执行。 
}
​
for(const value of ["Hello", 22]) {
    // 每次迭代的过程中,会销毁上次 const 定义的变量,然后重新定义
    console.log(value); // 输出为:Hello 22
}

总结

ES6 之后出现的 let 和 const 有助于提升代码的质量,在开发过程中,可以尝试优先使用 const 和 let,这样也可以避免使用 var 造成的奇怪的问题。

本文内容参考自《JavaScript高级程序设计》第4版,希望对大家有所帮助!

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@cooltea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值