你真的了解setTimeout吗?这里整理了一份关于setTimeout的面试题

最近一直在刷面试题补知识点,这样的好处就是针对目前的技术栈去学习,缺点就是杂乱无章,文章看完一篇,学完一篇就不知道该怎么继续往下学了。但现阶段我还是比较喜欢这种学习方法,后面再准备看书系统复习一遍。废话不多说,进入今天的主题,关于setTimeout你的真了解吗?之前我的文章或多或少都能看到这个词,所以说个人感觉我还是有点小了解的,但是今天在看文章后,我自闭了。下面直接看我整理的一些setTimeout面试题,试试自己到底对这些知识的掌握程度。其中还涉及到了一些预编译,闭包,IIFE,等知识点。关于答案我会一步一步的解答。

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

这是常规的面试题,假如入门js的童鞋都应该清楚最后打印的是什么了。假如是还没懂得呢,没关系,跟着走一遍。首先setTimeout是异步执行的。所以,当i等于0时,for循环已经是结束了,也就是说此时的i=5,为什么等于5?因为只有i=5,这个程序才算结束,也就是说在for循环里其实是添加了5个定时器,但此时的i=5。所以会每隔1秒打印一次5。

for循环

假如不清楚异步执行的可以看我之前的文章javascript的运行机制,这里也有讲到setTimeout。

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

这个和上面有什么不同呢,我们使用IIFE的方式,生成一个作用域,并把当前的i传入到这个函数里面,所以当setTimeout在查找变量的时候,会先找到IIFE创建的这个作用域里面的i。所以最后打印出来的结果就是每隔一秒依次打印出0,1,2,3,4。这里涉及到了js的作用域及变量的查找方式,如果不是很清楚这个知识点的可以自行查找一下。

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

这个和上面的第二个例子很相似,区别在于立即执行函数里少了形参i。所以当setTimeout内的函数执行时,查找到的i还是for循环里i的值,此时的i=5。最后的打印结果就是每隔一秒打印一次5,总共打印5次。

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

这个例子似乎又回到了例子1.但区别在于,我们给setTimeout传递的函数中,多了一个形参。这里需要你了解函数的预解析了,当开始执行setTimeout里的函数function(i){console.log(i)}时,会进行预解析,此时i=undefined的。这里你可能会困惑,i难道不应是外面的5吗?为什么是undefined的,因为在这个函数作用域里,由于传递了一个形参i,所以就会默认在这个函数作用域内定义一个i,它的值等于undefined,当执行到console.log(i)的时候,会先查找当前作用域里是否有i,是的,当前作用域里有一个i=undefined,所以最后的结果就是每隔一秒打印出一个undefined,总共打印5次。

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

这个例子比较特殊,打印结果是立刻打印出0,1,2,3,4。这说明我们的定时器并没有执行。为什么会这样呢?先来分析一下,这里setTimeout传递的是一个IIFE函数,也就是自我执行函数。自我执行的函数会立即执行,相当于一个同步任务,立即执行后会打印出当前的值,所以就会立即打印出当前的i值,由于for循环的执行速度很快,几毫秒就会执行完,所以在控制台你就会看到立即打印出0,1,2,3,4。然后在来分析setTimeout()为什么没有执行。自我执行函数中并没有返回值,这里注意是没有返回值,这个函数只是单纯的执行完了,没有返回值就默认为undefined,这样这个函数就等于setTimeout(undefined,i*1000),定时器没有执行的函数所以就不会执行。

这里你可能还会有疑惑,那我们在来举个例子

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

这里我建议你把代码放在控制台输出一下,可以看到,依旧会立刻打印出0,1,2,3,4。为什么会立刻打印出这个结果,因为temp这里是一个立即执行函数,所以会立刻执行然后打印出结果,但是后面为什么会打印出undefined呢?因为什么temp这个变量接受的就是一个空值,因为函数执行完以后并没有返回值,所以temp就是一个空值。这时,你可以把setTimeout注释掉看看,是否会有什么变化。你会发现结果还是没有变化,这说明这个定时器压根就没有执行。

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

这个例子setTimeout还是接受一个自我执行的函数,但是当这个函数执行完以后,返回的是一个函数声明;所以setTimeout第一个参数的返回值接收的其实是一个函数。当时由于闭包的特效,当前的i被保存在了这个作用域中,所以最后的结果会是每隔一秒依次打印出0,1,2,3,4。

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

这个我相信大多数人都应该知道答案了,就是每隔一秒依次打印出0,1,2,3,4.因为定时器执行了5次,所以会输出5次,但是定时器内的递加就执行了4次。

var put=function(i){
      setTimeout(function(){
         console.log(i);
},i*1000)
}

for(var i =0;i<5;i++){
   put(i);
}

上面这个例子呢,只不过是把函数作为表达值赋值给了一个变量,然后在for循环里执行这个表达式。其实这个可以等价于

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

下面我们在来看看setTimeout涉及到this时,需要注意些什么

function sayHi(){
    console.log('Hello,', this.name);
}
var person1 = {
    name: 'YvetteLau',
    sayHi: function(){
        setTimeout(function(){
            console.log('Hello,',this.name);
        })
    }
}
var person2 = {
    name: 'Christina',
    sayHi: sayHi
}
var name='Wiliam';
person1.sayHi();       //Hello  Wiliam
setTimeout(person2.sayHi,100);   //Hello Wiliam
setTimeout(function(){
    person2.sayHi();
},200);     // Hello Christina

最后输出的会是什么呢?

person1.sayHi(),setTimeout的回调函数中,this使用的是默认绑定,非严格模式下,执行的是全局对象,所以this.name指向的就是全局的name。

setTimeout(person2.sayHi,100),这里看到了.说明运用了隐式绑定,但person2.sayHi只不过是一个函数的地址,并没有执行,所以和person2没有关系。当我们执行定时器的时候,this指向的还是全局的window,this.name也是全局的name。

setTimeout(function(){ person2.sayHi(); },200):因为这里person2.sayHi()是函数的执行,所以这里的this运用的是隐式绑定,指向的就是person2中的name。

陈健的个人博客,记录生活所见所感、学习笔记。专注于Web前端_SEO教程_读书心得。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

返回主页看更多
狠狠的抽打博主 支付宝 扫一扫