0%

一道常见的闭包面试题

我们都知道下面这个代码的执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
})
}
```
产生这种效果是因为for循环将i定义到了全局变量中,所以console时也是读取的全局的i,因此为6
如果我们想依次输出从0-6的话只需要这么改
```js
for (var i = 1; i <= 5; i++) {
(function () {
var j = i;
setTimeout(function () {
console.log(j);
}, 10)
})()
}

// 这种写法更优雅
for (var i = 1; i <= 5; i++) {
// 通过立即执行函数的形参传入
(function (j) {
setTimeout(function () {
console.log(j);
}, 10)
})(i)
}

这背后的原理是这样的:
立即执行函数会行成一个封闭的作用域,从而通过j保存了当时的i,当然了如果你通过一个函数作用域去封闭也是可以的,代码如下:

for (var i = 1; i <= 5; i++) {
  // 通过立即执行函数的形参传入
  function a (j) {
    setTimeout(function () {
      console.log(j);
    }, 10)
  }
    a(i)
}

在了解了上面的内容后我们也就不难解释为什么使用let可以达到这种效果

for (let i = 1; i <= 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 10)
}

如果使用let就会生成块级作用域,每次循环的时候都会生成一个作用域,而且在每次循环开始都会将上次已经++的i的值赋给新生成的作用域的i