JS由浅入深的一道面试题
程序员文章站
2024-03-23 13:28:28
...
前言: 前端面试中,JS是一大热点,下面就跟着小编来看一下一道由浅入深的面试题吧。
正文: 请说出下面代码的执行结果:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
因为JS是单线程的语言,所以遇到异步函数的时候,setTimeout会被放到等待队列中,当主线程上的函数执行结束之后,再去执行等待队列中的函数,如下图所示:
当主线程执行完毕,此时变量i已经变成5,所以上述代码的结果是输出5个5。
问题来了:如果我想输出01234,应该如何实现呢?
方式一:let
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
这里就要说一下let和var的区别了,let有块级作用域,就是一个{}的作用域,而var就没有块级作用域,只有函数作用域和全局作用域,所以使用var声明的话会打印出5个5,用let声明的话会打印出01234。
方式二:函数自执行,闭包
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
闭包延续变量的生命周期,将变量保存,所以会输出01234。同时也可以把自执行函数抽出一个函数,方便以后的覆用。
var output = function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
output(i);
}
问题又来了:如果我想输出012345,应该如何实现呢?
可能会有人说直接在后面添加console.log(i);
不就可以了吗,如下:
var output = function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
output(i);
}
console.log(i)
但是结果如下:因为会先执行主线程
那应该如何做呢?
方案一:setTimeout延迟执行
var output = function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
output(i);
}
setTimeout(function() {
console.log(i);
}, 500 * i);
把输出5的也放到异步函数中,这样就会在上面的异步函数执行完之后在执行下面的异步函数了。
方案二:Promise 定义多个异步任务
const tasks = [];
for (var i = 0; i < 5; i++) {
((j) => {
tasks.push(new Promise((reslove) => {
setTimeout(() => {
console.log(j);
reslove();
}, 1000);
}));
})(i);
}
// 等待所有异步任务执行完成
Promise.all(tasks).then(()=>{
console.log(i);
})
同理:也可以把里面的自执行函数封装成一个函数,方便以后调用,这里不进行展示。
总结: 要真正掌握JS,就要对JS活学活用,因为一点点的区别,最后的结果可能会大相径庭。
上一篇: C++的inline函数