这道题是常见的面试题,但是想要答好,想要面试官眼前一亮,还是得答一些被平常人所不注意的知识点! ok,首先看到这个题,我脑子里想到的就是:
1.块级作用域:(必答)
块级作用域是: 由{}包括起来的部分
具有块级作用域: let和const;
不具有块级作用域: var
⭐补充: (亮点)
块级作用域解决了ES5中的两个问题:
① 内层变量可能覆盖外层变量
② 用来计数的循环变量泄露为全局变量
ok,我将用例子来说一下上面这两个问题!
<script>
var x = 10;//外层变量x
if (true) {
var x = 20;//内层变量x
console.log(x);
}
console.log(x);//这里本来应该是输出10,但是因为内层变量x的原因,所以外层变量x的值被内层变量x的值覆盖了
</script>
//浏览器输出的结果是:20 20
如果把上面这段代码换成let来声明,会怎么样?
let x = 10;
if (true) {
let x = 20;
console.log(x);//输出20
}
console.log(x);//输出10,为什么不是20呢?因为let x = 20是在块里面,它不会覆盖外层let x = 10 的值,所以输出就是10
ok,到此,解决了第一个问题“① 内层变量可能覆盖外层变量”!
接下来,解决第二个问题!
直接上代码解释:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[5]();//10
a[6](); // 10
可能大家看到这个,一时懵了,为什么都是10。
别怕,我当时也是,但是认真分析一下,还是可以懂的。
解析: (认真理解下面这段解析)
上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。
每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。
也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。
ok,那如果我把上面对i的声明用let声明,那结果会是怎么样呢?
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[5]();//5
a[6]();//6
现在就不是10了,因为跟let是块级作用域有很大关系,那么同样解析一下:
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是5、6。
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
2.变量提升: (必答)
存在变量提升: var;
不存在变量提升: let和const;
即变量只能在声明之后使用,否则报错❌
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
//上面代码中,变量foo用var命令声明,会发生变量提升
//即脚本开始运行时,变量foo就已经存在了,但是没有值,所以会输出undefined。
//上面代码运行时,是下面这样子的:
var foo;
console.log(foo);
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
//上面第17、18行代码中,变量bar用let声明,不会发生变量提升。
//这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误
3.给全局添加属性: (感觉不用非得答)
会将变量添加为全局对象的属性的是:var;
不会将变量添加为全局对象的属性的是:let和const;
浏览器的全局对象是window,node的全局对象是global,那var呢?var声明的变量为全局变量,且会将变量添加为全局对象的属性。
4.重复声明: (必答)
var 声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量;
let和const不允许重复声明变量
5.暂时性死区: (必答)
在使用let、const命令声明变量之前,该变量都是不可用的,这在语法上,称为“暂时性死区”。
使用var声明的变量不存在暂时性死区。
6.初始值设置: (必答)
在声明变量的时候,var 和 let 可以不用设置初始值,而const必须要设置初始值
7.指针指向: (必答)
let创建的变量是可以更改指针指向(也就是说可以重新赋值),var也可以,但是const不可以