var、let、const这些变量声明各有什么区别及特点,本文将从三个方面阐述他们差别:
- 变量提升机制
- 块级作用域绑定
- 块级绑定的最佳实践
一、var变量提升机制及特点
在函数作用域或全局作用域中通过var声明的变量,都会被当成在当前作用域顶部声明的变量,这就是变量提升机制,下面用一个函数例子说明:
function getAge(name){
if(name){
var age = '28';
return age;
}
// 这里也能访问到if语句里的age变量,不过因为变量未初始化,结果为undefined
console.log(age);
}
其实js在预编译阶段,会把代码修改成下面这样子:
function getAge(name){
var age;
if(name){
age = '28';
return age;
}
console.log(age); // 结果也为undefined
}
从上面的示例中可以看到,变量age的声明被提升至函数顶部,而初始化操作依旧在if语句中完成。 不过还有一点要注意的是,除了var有提升机制,函数也有,请看下面示例:
console.log(age) // ƒ age() {console.log(123);}
age(); // 28
var age = '28'
function age(){
console.log('28');
}
age() // Uncaught TypeError: age is not a function
上述示例说明:函数提升与变量提升同时存在时,函数提升优先级高于变量提升并且,并且不会被变量声明覆盖,但是当变量赋值之后age函数被覆盖。
注:还有一个要注意的点,当var被用于全局作用域时,它会创建一个全局对象(浏览器环境中的window对象)的属性,所以使用var在全局作用域声明的时候要特别注意,全局对象中是否存在这属性,防止被覆盖
var age = '40';
console.log(age); // 40
console.log(window.age) // 40
var RegExp = "hello,world";
// 本来window全局对象中有js内置的RegExp函数,结果被var声明的RegExp变量给覆盖了。
console.log(window.RegExp); // hello,world
二、块级作用域绑定
1、let声明
在上面我们提到var声明会被提升至作用域顶部,由此可能会引发一些奇怪的BUG,正因为如此, ES6 引入了块级声明(let、const),块级声明也就是让所声明的变量在指定块级作用域外无法被访问,让变量的生命周期更加可控。还是以上面的例子说明:
function getAge(name){
if(name){
let age = '28';
return age;
}
// 如果使用let块级声明,这里会报错:
// Uncaught ReferenceError: age is not defined
console.log(age);
}
上述例子说明age变量只有在if代码块里可以访问,其它地方无法访问该变量,这说明let会在一个函数内部或在一个代码块(由一对花括号包裹)内部会创建一个块级作用域。 下面这个例子也很好的说明了这个问题:
for(var i = 0;i<10;i++){}
console.log(i); // 10
因为var声明的变量被提升到全局作用域顶部,循环体外也可以访问,也导致了变量i在循环的每次迭代中都被共享了,所以得到的结果是:10, 但使用let就可以解决这个问题:
for(let i = 0;i<10;i++){}
console.log(i); // Uncaught ReferenceError: i is not defined
使用let声明的话,i变量只在循环体内可以访问,其它地方都访问不到。 同时let也禁止重复声明:
var age="40";
var age = "50";
// 如果var声明,后面的声明会覆盖前面的声明,所以这里打印结果结果是:50
console.log(age);
let name = "张三";
let name = "李四";
// 使用let重复声明,会出现报错:
// Uncaught SyntaxError: Identifier 'name' has already been declared
console.log(name);
let和var声明还有一点不同的是,在全局作用域中let声明不会像var一样会在全局window对象中创建一个属性。
var age = '40';
console.log(window.age === age); // true
let name = '张三';
console.log(window.name === name); // false
且let并不会像var声明被提升到作用域顶部
console.log(name); // undefined
var name = '张三';
console.log(age); // 报错:Uncaught TypeError: age is not a function。
let age = '40';
因为var声明会被提升至作用域顶部,所以此时访问name是存在的,但是name此时并未被赋值,所以得到undefined,但是如果使用let或const来声明变量,因为它们并不具备提升机制,所以在声明前访问会报错,也称暂时性死区。
2、const声明
const的作用很多时候和let是一样的,但通过const声明的变量为基础数据类型时,它是不可变的,当它声明的变量为引用类型时,可以修改它的属性,如果要确保引用类型值的属性值不可变,请使用Object.freeze()冻结
let age = '40';
age = '50';
console.log(age); // 50
const name = '张三';
name = '李四';
console.log(name); // 报错:Uncaught TypeError: Assignment to constant variable.
const person = {
name:'张三'
}
person.name = '李四';
console.log(person.name); // 李四
三、块级绑定的最佳实践
在ES6中一般情况下需要使用变量声明的地方都应该使用let或const替代var,在默认情况下建议使用 const,因为一般变量初始化之后都不应该被改变,有助于防止某些类型的错误,但变量值确实需要被更改的情况下才使用let。