var、let、const 的区别
var 关键词
- var 声明作用域
var 定义的变量,没有块的概念,可以跨块访问,不能跨函数访问
1 | function test() { |
函数 test () 调用时会创建变量 message 并给它赋值,调用之后变量随即被销毁。因此,在函数 test () 之外调用变量 message 会报错
在函数内定义变量时省略 var 操作符,可以创建一个全局变量
1 | function test() { |
省略掉 var 操作符之后,message 就变成了全局变量。只要调用一次函数 test (),就会定义这个变量,并且可以在函数外部访问到。在局部作用域中定义的全局变量很难维护,不推荐这么做。在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出 ReferenceError。
- var 声明提升
var 在 js 中是支持预解析的,如下代码不会报错。这是因为使用 var 声明的变量会自动提升到函数作用域顶部:
1 | function foo() { |
javaScript 引擎,在代码预编译时,javaScript 引擎会自动将所有代码里面的 var 关键字声明的语句都会提升到当前作用域的顶端,如下代码:
1 | function foo() { |
let 声明
- let 声明作用域
let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,而 var 可以跨块访问
1 | // var定义的变量 |
let 也不允许同一个块作用域中出现冗余声明(重复声明)
1 | var name; |
- 暂时性死区
let、const 与 var 的另一个重要的区别,let、const 声明的变量不会在作用域中被提升。ES6 新增的 let、const 关键字声明的变量会产生块级作用域,如果变量在当前作用域中被创建出来,由于此时还未完成语法绑定,所以是不能被访问的,如果访问就会抛出错误 ReferenceError。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。
1 | // name会被提升 |
- 全局声明
与 var 关键字不同,var 定义的全局变量会挂载到 window 对象上,使用 window 可以访问,而 let 在全局作用域中声明的变量不会成为 window 对象的属性
1 | var name = 'Matt'; |
- for 循环中的 var、let 声明
for 循环中 var 定义的迭代变量会渗透到循环体外部:
1 | for (var i = 0; i < 5; ++i) { |
改成使用 let 之后,这个问题就消失了,因为迭代变量的作用域仅限于 for 循环块内部:
1 | for (let i = 0; i < 5; ++i) { |
使用 var 和 let 定义 for 循环中的变量,循环里使用定时器 setTimeout 后循环结果如下代码:
1 | for (var i = 0; i < 5; ++i) { |
let 是在代码块内有效,var 是在全局范围内有效。let 只能声明一次 ,var 可以声明多次。
当同步代码执行完毕后,开始执行异步的 setTimeout 代码,执行 setTimeout 时需要从当前作用域内寻找一个变量 i,for 循环执行完毕,当前 i=5,执行 setTimeout 时输出为 5,任务队列中的剩余 4 个 setTimeout 也依次执行,输出为 5。
变量 j 是用 let 声明的,当前的 i 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 0-4。
const 声明
const 的行为与 let 基本相同,唯一一个重要的区别是:
const 是用来定义常量的,而且定义的时候必须赋值,不赋值会报错,定义之后是不允许被修改的,修改 const 声明的变量会导致运行时错误。
1 | const age = 26; |
而 const 声明的变量是一个对象时,修改这个对象内部的属性并不会报错。
这是因为 const 声明的是栈区里的内容不能修改,基本数据类型的值直接在栈内存中存储,而引用数据类型在栈区保存的是对象在堆区的地址,修改对象的属性,不会修改对象在栈区的地址,如果重新给对象 person 赋值,则会报错。
1 | const person = { |
JavaScript 引擎会为 for 循环中的 let 声明分别创建独立的变量实例,虽然 const 变量跟 let 变量很相似,但是不能用 const 来声明迭代变量(因为迭代变量会自增):
1 | for (const i = 0; i < 10; ++i) {} // TypeError:给常量赋值 |
不过,如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。也就是说,每次迭代只是创建一个新变量。这对 for-of 和 for-in 循环特别有意义:
1 | let i = 0; |