var、let和const的区别
这个也是前端老生常谈的问题了,那么这篇文章我将结合红宝书学到的内容和书上的案例来给大家讲解
var关键字
在ES6之前,我们最习惯使用的就是var关键字来定义一个变量,但是var关键字会带来一些问题
var关键字存在变量的提升,比如下面的代码
function test() {
console.log(num); // undefined
var num = 10;
}
test();
如果是刚开始学javascript这门语言的同学可能会有点疑惑,为什么不会报错,而是直接输出undefined呢,这个就是因为var关键字存在变量提升,以上的代码实际上的执行顺序是首先var num,但是这个时候并没有赋值,而是执行了console.log(num); 然后在进行了num = 10的赋值操作。
这里就有一道很常见的面试题
for(var i = 1; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
这里大家可以猜猜会输出什么
答案就是输出5个5,之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值
let关键字
ES6推出了let和const(在后面会讲噢)的这两个关键字,let和const是解决了var关键字带来的变量提升,并且官方也是更提倡我们使用let和const来定义变量,而不再使用var关键字来定义声明变量
- 刚刚也说了,let、const和var的最大区别就是没有变量提升,这种特性也被称为暂时性死区
console.log(num); // Uncaught ReferenceError: Cannot access 'num' before initialization
let num = 10;
这里就和我们想的一样,没有声明赋值的变量,直接输出是会报错的
- 与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声
明的变量则会)。
var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined
不过,let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续。因此,为了
避免 SyntaxError,必须确保页面不会重复声明同一个变量。
3. 条件声明
在使用 var 声明变量时,由于声明会被提升,JavaScript 引擎会自动将多余的声明在作用域顶部合
并为一个声明。因为 let 的作用域是块,所以不可能检查前面是否已经使用 let 声明过同名变量,同
时也就不可能在没有声明的情况下声明它。
<script>
var name = 'Nicholas';
let age = 26;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
var name = 'Matt';
// 这里没问题,因为可以被作为一个提升声明来处理
// 不需要检查之前是否声明过同名变量
let age = 36;
// 如果 age 之前声明过,这里会报错
</script>
使用 try/catch 语句或 typeof 操作符也不能解决,因为条件块中 let 声明的作用域仅限于该块。
<script>
let name = 'Nicholas';
let age = 36;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
if (typeof name === 'undefined') {
let name;
}
// name 被限制在 if {} 块的作用域内
// 因此这个赋值形同全局赋值
name = 'Matt';
try {
console.log(age); // 如果 age 没有声明过,则会报错
}
catch(error) {
let age;
}
// age 被限制在 catch {}块的作用域内
// 因此这个赋值形同全局赋值
age = 26;
</script>
为此,对于 let 这个新的 ES6 声明关键字,不能依赖条件声明模式
看到这里,我们又看回var关键字当时那个定时器输出i变量的例子,如果使用let关键字来重写的话,那么我们就会得到我们想要的结果
for(let i = 1; i <= 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
const关键字
const关键字的大部分特性都和let关键字都是一样的,但是const不一样的点在于,const关键字定义的是常量,是不能在进行修改的,修改的时候会发生报错,所以我们不能在for循环,比如i++,这种情况是不能使用const定义i的值
const num = 10;
num = 20; // Uncaught TypeError: Assignment to constant variable.
const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,
那么修改这个对象内部的属性并不违反 const 的限制。
const person = {};
person.name = 'Matt'; // ok
不过,如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。也就是说,每
次迭代只是创建一个新变量。这对 for-of 和 for-in 循环特别有意义:
let i = 0;
for (const j = 7; i < 5; ++i) {
console.log(j);
}
// 7, 7, 7, 7, 7
for (const key in {a: 1, b: 2}) {
console.log(key);
}
// a, b
for (const value of [1,2,3,4,5]) {
console.log(value);
}
// 1, 2, 3, 4, 5
总结:不使用var关键字来定义变量,使用let和const关键字,如果是定义的常量,声明赋值后不需要再次修改的则使用const优先,因为使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。