Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何解释 ’for循环‘ 经典问题? #1

Open
shen774411223d opened this issue Feb 10, 2021 · 0 comments
Open

如何解释 ’for循环‘ 经典问题? #1

shen774411223d opened this issue Feb 10, 2021 · 0 comments

Comments

@shen774411223d
Copy link
Owner

shen774411223d commented Feb 10, 2021

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

低端回答

因为在for循环里使用了 var 关键字,var会在上下文执行栈里创建环境记录项,在计时器内部每次访问的都是全局的i
所以每次的值都是相同的
为什么每次都是全局i? 因为var关键字没有块级作用域而且会变量提升,每次for循环就相当于:


var i = undefined
for() {
  var i = 上一轮的i
  ... 
}

可以理解为在for里面使用var。根据变量提升,var会在for的上面先定义 然后每次for在重新定义同样的变量。所以每次修改的都是同一个变量

高端回答

除了上述所说的var关键字的问题
还关系到setTimeout是一个宏任务,也可以说是一个异步回调。for执行时期,异步回调还没有开始执行。等for执行完毕 开始依次执行栈里的异步回调,这时回调里并没有定义i 变量,所以根据作用域链会向上寻找,寻找到了全局的i

这个问题的根本原因是for循环里的回调函数的上下文执行栈中并没有保存i,根据作用域链 寻找上级的上下文执行栈

那么如何解答呢?

1. 立即执行异步回调,或者使用变量将i保存到函数内部

2. 使用let关键字。使用let定义的变量会绑定在块级作用域内;ECMA对for的定义是:每次循环都会创建一个新的块级作用域,本轮循环的值指向上一轮的值(个人理解有赋值/指针指向的意思,而不是和上一轮是相同值)。

所以总结一下就是 for每次循环都会创建一个新的块级作用域,而且会在作用域里重新 let i 。所以每轮的回调中的i都指向了不同变量(不同的上下文执行栈

这个问题的核心点就是:只要让i能保存在每轮循环的上下文执行栈里就没错

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant