JavaScript 中 var、let、const 三种声明变量的方式之间的具体差别
文章目录
前言
在ES6出现之前在 JavaScript 中声明变量都是用 var 来声明;在ES6出现之后,一般会使用 let 代替 var来进行变量的声明,使用 const 来进行常量的声明。
var | let | const | |
---|---|---|---|
重复声明 | Y | ||
变量提升 | Y | ||
暂时性死区 | Y | Y | |
window 对象的属性和方法 | Y | ||
块级作用域 | Y | Y | |
定义后可修改 | Y | Y |
1. 重复声明
/* var */
var a = 123
var a = 321
console.log(a); // 打印结果: 321 新的会覆盖旧的
/* let */
let b = 123
let b = 321
console.log(b); // 报错:Uncaught SyntaxError: Identifier 'b' has already been declared
/* const */
const c = 123
const c = 321
console.log(c); // 报错:Uncaught SyntaxError: Identifier 'c' has already been declared
- 总结 1:在同一个块级作用域下,var 可以重复声明同一个变量,但 let 和 const 不能。
2. 变量 提升(hoisting)
/* var */
console.log(a); // 打印结果: undefined
var a = 123
// 逻辑上等于
var a
console.log(a);//undefined
a = 123
/* let */
console.log(b); // 报错:Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 123
/* const */
console.log(c); // 报错:Uncaught ReferenceError: Cannot access 'c' before initialization
const c = 123
- 总结 2:变量提升是指无论 var 出现在一个作用域的哪个位置,这个声明都属于当前的整个作用域,在当前的整个作用域中任何地方都可以访问得到这个变量 。注意只有变量声明才会提升,但变量赋值并不会提升。let 和 const 不存在变量提升。
3. 暂时性死区(临时死区 Temporal Dead Zone,简写为 TDZ)
/* var */
var a = 1;
console.log(a); // 1
console.log(b); // undefined
var b = 2;
/* let */
let a = 1;
console.log(a); // 1
console.log(b); // 报错:Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 2;
/* const */
const a = 1;
console.log(a); // 1
console.log(b); // 报错:Uncaught ReferenceError: Cannot access 'b' before initialization
const b = 2;
var a = 123
{
console.log(a); // U123
var a = 312
}
let a = 123
{
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 312
}
- 总结 3:暂时性死区和变量提升有点相类似,如果区块中存在let 和 const 命令,这个区块对这些命令声明的变量或常量,一开始就形成一个作用域。let 和 const 的变量不能在声明之前被使用,var 可以。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码被执行过后,才可以获取和使用该变量,也就是先声明,再使用。
4. window 对象的属性和方法(全局作用域中)
var a = 123
console.log(window.a); // 123
function fn() {
console.log('function');
}
window.fn(); // function
console.log(window.fn === fn); // true
let b = 123
console.log(window.b); // undefined
let c = fn
console.log(window.c === fn); // false
- 总结 4:在全局作用域中,var 声明的变量和通过 function 声明的函数,会自动变成 window 对象的属性或方法,但是 let 和 const 不会。
5. 块级作用域
/* var */
for (var i = 0; i < 3; i++) {
console.log(i); // 1 2 3
}
console.log(i); // 3
/* let */
for (let i = 0; i < 3; i++) {
console.log(i); // 1 2 3
}
console.log(i); // 报错:Uncaught ReferenceError: i is not defined
/* const */
if (true) {
var a = 'asd'
const c = 'zxc'
}
console.log(a) // asd
console.log(c) // Uncaught ReferenceError: c is not defined
/* 这里涉及相关的只是点 */
// 作用域链:内层作用域 --> 外层作用域 --> ... --> 全局作用域
// 作用域有三种:全局作用域、块级作用域、函数作用域
// 块级作用域有哪些:
// {}
// for(){}
// while(){}
// do{}while()
// if(){}
// switch(){}
// 函数作用域:
// function(){}
// let animal={} 这里对象的{},不构成作用域的
- 总结 5:let 和 const 的声明会形成块级作用域,var 不会。
6. 定义变量后可修改
/* 简单数据类型 */
var a = 111
a = 999
console.log(a); // 999
let b = 222
b = 888
console.log(b); // 888
const c = 333
c = 777
console.log(c); // 报错:Uncaught TypeError: Assignment to constant variable.
/* 复合数据类型 */
const animal = {}
animal.dog = '二哈'
animal.cat = '大橘'
console.log(animal) // {dog: "二哈", cat: "大橘"}
animal = { bird: '鹦鹉' } // 报错:Uncaught TypeError: Assignment to constant variable.
const arr = ['foo', 'asd']
arr.push('zxc')
arr[3] = '123'
arr.shift('asd')
console.log(arr) // ["asd", "zxc", "123"]
- 总结 6:var、let 定义变量之后可以修改,而 const 表面上像是声明一个“常量”,但 const 并不是保证变量的值不得改动,而是指变量指向的内存地址不得改变。对于简单数据类型(Number、String、Boolean),变量就指向的保存了对应值的内存地址,因此等同于常量;而对于复合数据类型(主要是对象和数组),变量只是保存了指向堆内存的地址,至于堆内的数据是不是可变的,就不能控制了。
结论:
为了规范代码书写,减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为,声明变量最好优先使用 const 、let 命令。