函数闭包、IIFE、JS解释器

在掘金上看到了篇文章,里面讲了几个js代码片段,觉得很有意思,其中考察了关于setTimeout函数闭包IIFE(立即执行函数表达式)promisejs解释器等概念的理解

A:下面的程序输出啥?

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

我:没有坑,别想多了。输出0 1 2 3 4

A:恩,那下面这个呢?

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

我:settimeout时间到后,会在下一个tick中执行程序,所以程序打印的时候,i已经变成5了,输出5 5 5 5 5

A:不错,那我想输出的结果是0 1 2 3 4,应该怎么办呢?

我:用闭包就解决了,如下

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

因为函数作用域内,保存着对于变量i的引用,故函数执行的时候仍能访问到当初的i。

A:很好。那如果去掉参数i呢?

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

我:这样的话,函数闭包内没有存储对i保持引用,所以输出结果又会变成5 5 5 5 5

A: 对。那我再给你改一下程序,你看看输出啥?

//给setTimeout传递一个立即执行函数 
for(var i=0;i<5;i++){
setTimeout((function(i){
console.log(i);
})(i), 1000*i);
}

我:这里给setTimeout传递的是一个立即执行函数表达式,它会马上运行,执行结果是undefined,所以setTimeout输出0之后,应该会报错吧,因为参数传递不对,应该接受一个函数类型的参数。

上面的代码差不多等于:

//其实等价于
for(var i=0;i<5;i++){
setTimeout(undefined, 1000*i);
}

A:恩!再看下面的这个程序输出什么?

setTimeout(function() {
console.log(1);
}, 0);

new Promise(function executor(resolve){
console.log(2);
for( var i=0;i<10000;i++){
i == 9999 && resolve();
}
console.log(3);
}).then(function(){
console.log(4);
});

console.log(5);

我:思考半天…,应该输出2 3 5 4 1。这个题目是关于js解释器理解的。setTimeout中的函数会在下一个时钟周期运行,肯定比较靠后。2,3在立即执行函数中,所以他会立马打印。4then中输出,它会放在当前周期的最后执行。最后5会立马打印。所以会这样2 3 5 4 1

疑问:关于setTimeout和promise.then执行先后的问题,上网找了一下原因,setTimeout属于Macrotask,而promise属于MicrotaskMicrotask在当前周期队列执行,Macrotask在下一周期执行。

参考自:Excuse me?这个前端面试在搞事!