JS由浅入深的一道面试题

前言: 前端面试中,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活学活用,因为一点点的区别,最后的结果可能会大相径庭。

原创文章 212 获赞 138 访问量 9万+

猜你喜欢

转载自blog.csdn.net/m0_37508531/article/details/105419247