其实也是由于面试官喜欢提到这两者的区别,了解基本的比如:let和const不存在变量提升,存在暂时性死区(TDZ),不允许重复声明,但面试官可能更想听到,你深入说下怎么变量提升,为什么变量提升就不好了,以并能提及对应的应用场景就更好了。
为此,下面主要内容会讲到块级作用域绑定,let和const与var的联系和区别。
过去,javascript 的变量声明机制一直让人感到困惑,因为大多数c语言在声明变量时也会创建变量绑定,但js中,可能使用变量之后你再声明变量也能打印value。
- var声明及变量提升机制(Hoisting)
在函数作用域或全局作用域中如果通过var声明变量,会当成当前作用域顶部声明的变量,此为变量提升。譬如以下,在预编译阶段,js引擎将函数中的var value提升到函数顶部,出现下面情况。但换成let不会出现下面情况,也就是说let不存在变量提升,const同是。
function getV(){
if(condition){
var value="blue"
console.log(value);
}else{
console.log(value);//此处仍可以访问value,underfined
}
- let代替var声明变量可以将作用域限制在当前代码块中,通常在一个块级作用域内(函数内部、块内{})只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
- let和const不允许重复声明 , 而const声明实际是不允许修改值的绑定,但允许修改值。
- 开发者最希望实现for循环的块级作用域,因为可以将随意声明的计数器变量限制在循环内部。根据前面变量提升,一个for循环,如果用var定义计数器变量,由于变量提升,当循环结束后,计数器变量i仍然可在外面访问,而let声明的变量则可达到我们限制在块级作用域内的效果。
- let和const在循环中,for-in、for-of每次迭代时创建新的绑定,从而使循环体中创建的函数可访问到相应迭代的值,而非最后一次迭代的值。
也就是下面的结果,执行了for循环后,通过func.push可得到一个数组,每项均是一个函数,期望输出i为0-9,但实际上由于var的变量提升,for循环后得到的结果为最后一次i=10,连续十次输出,这肯定不是我们想要的。
var func = [];
for (var i = 0; i < 10; i++) {
func.push(function() {
console.log(i);
})
}
func.forEach(function(func) {
func();
})
解决方法:
立即执行函数
var func = [];
for (var i = 0; i < 10; i++) {
func.push(function(value) {
return function() {
console.log(value);
}
}(i))
}
func.forEach(function(func) {
func();
})
有了let就简单了,因为每次循环时let都会创建一个新的变量i,将其初始化为i的当前值,所以循环内部创建的每个函数都得到属于它自己的i值
var func = [];
for (let i = 0; i < 10; i++) {
func.push(function() {
console.log(i);
})
}
func.forEach(function(func) {
func();
})
console.log(func);