做题的时候引发的几个小思考

369 阅读2分钟

「这是我参与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循环中有效,当在全局环境中去寻找时是找不到的,所以程序报错