「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
前言
欸哟喂,这两天做了一个很搞的题目做的我有点想法,废话不多,先上代码
代码
var a = [];
for(var i = 0;i<10;i++){
a[i] = function(){
console.log(i)
}
// 模块1
a[0]()
}
// 模块2
a[0]()
你们觉得 模块1 和 模块2 的输出会是啥
输出
- 模块1
0 1 2 3 4 5 6 7 8 9 - 模块2
10
思考
但是当在外面执行的时候,其实函数执行的时候i已经变成了10,当函数console.log(i)的时候i是10;那么你也许会问明明循环中已经定义好了嘛
a[6]= function(){
console.log(6)
}
其实不是这样的,在函数执行之前这个函数是不会读取函数体中的变量的,也就是说,循环了那么多次,定义了那么多次函数,我们读取的始终都是这个变量i,而这个变量i使用var定义的,var又有变量提示的情况
如果不是用var而是用let呢?
如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
为什么会这样输出呢?导致报错呢?
let是块级作用域而且let不能变量提升
当然我们可以继续发散一下思考
我们先看看下面的几个小栗子
let暂时性死区
我们先来一个测试代码
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
为什么会出现报错呢?
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。(简称TDZ)
有些“死区”比较隐蔽,不太容易发现。(重点理解)
function test(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
上面代码中,调用bar函数之所以报,是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。如果y的默认值是x,就不会报错,因为此时x已经声明了。
当然这样写就不回来
function test(x = 2, y = x) {
return [x, y];
}
bar(); // 这样就不会报错了
let不能变量提升
for(let i=0;i<10;i++){
// ...
}
console.log(i);
// ReferenceError: i is not defined
当前的i只在for循环中有效,当在全局环境中去寻找时是找不到的,所以程序报错