美文网首页
setTimeOut面试题

setTimeOut面试题

作者: 秋玄语道 | 来源:发表于2018-06-25 17:12 被阅读0次

今天面试遇到一道有意思的题目:

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

请问会输出什么?要求改动上述代码,使其依次输出0,1,2,3,4
正确答案是5,5,5,5,5,且是隔一秒出来一个

setTimeout(code,millisec)函数:用于在指定的毫秒数后调用函数或计算表达式,接受两个参数,第一个参数为一个函数或计算表达式,我们通过该函数定义将要执行的操作。第二个参数为一个时间毫秒数,表示延迟执行的时间。至于什么异步调用,队列这些概念,这里不做详述,可阅读:http://www.alloyteam.com/2015...

函数作用域:函数内部定义的变量与外部定义的变量,外部指包含这个函数的空间,是父子关系,二不是兄弟,编过程的应该都理解;无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定;

闭包:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。详细运用,推荐读:http://www.ruanyifeng.com/blo...,个人还是推荐红宝书上面的讲解。

等明白上面第一个settimeout概念后,最后一行为什么先打印最后一行的结果了;
明白变量作用域后,就会明白console.log(i)中的i是for循环声明的那个i变量,因为var声明的变量不存在代码块({})作用域的概念,所以最后打印的值是5;

在for循环声明的五个TimeOut Callback函数都有对变量i的引用(这里的引用不是引用类型的引用,而是i作为函数作用域链的一个变量,由于闭包造成的),而不是拷贝。因为5个timeout函数都涉及到延迟执行的情况,所以当主线程执行完后(end被打印时),timeout这些回调依次执行(队列:FIFO),此时i的值已经为5了,知道以上这些,后面就简单多了。

其实写出这个函数期望输出0,1,2,3,4,要达到这个结果,方法有多种,这里列出典型的三种:
方法1:IIFE:

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

方法2:函数调用按值传递:

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

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

方法3: ES6 使用let指令声明:

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

细度上面的三种方法,其实他们相似度很高。首先方法1(声明即执行)和方法2(提前声明,调用时执行),其实他们的思路完全一致,都利用了JavaSrcipt中函数基本类型变量传值,都是值的拷贝,而不是值的引用,然后通过在for循环中执行一个闭包函数,建立一个闭包作用域,来保证引用的i值为注册该回调函数时的值。立即即执行,如果看着别扭,下面这样写也是可以的:

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

然后方法3,是利用ES6 let命令声明变量块级作用域的概念,和前面for循环使用var声明i不同的是,var声明的i在整个test()函数作用域内有效,每一次循环, 新的i值都会覆盖旧值;而let声明的, 当前的i只在本轮循环有效, 所以每一次循环的i其实都是一个新的变量,所以也导致打印end时,报i 不存在,未声明的错误,这就是块级作用域的效果,所以5个timeout回调函数虽然都引用了变量i,但实际上这5个i是独立的,仅在自己的块级作用域内有效,其写法类似于:

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

参考文章来自:详解前端网红经典面试题:setTimeout与循环闭包

相关文章

  • Javascript执行机制(setTimeout/Promis

    遇到的一道关于javascript执行机制的面试题 Javascript执行机制(setTimeout/Promi...

  • setTimeOut面试题

    今天面试遇到一道有意思的题目: 请问会输出什么?要求改动上述代码,使其依次输出0,1,2,3,4正确答案是5,5,...

  • JS 函数的执行时机

    1. 常见的面试题代码解释 // 6 个 6JS是单线程运行的,setTimeout(code,millisec)...

  • setTimeout基础知识

    setTimeout & setInterval 怎么用 异步是什么回调是什么(不需要知道) 相关面试题 面试官的...

  • 文摘-20170305

    前端 释义图例详解那道setTimeout与循环闭包的经典面试题js中proto和prototype的区别和关系?...

  • 用setTimeout实现setInterval

    起因: 闲来无事和同事交流遇到过那些经典的面试题,同事说有次面试让用用setTimeout实现setInterva...

  • 关于setTimeout的面试题

    经常看到网上的前端面试题中会有关于setTimeout的这道题,这题经常有人写,一道题包含了javascript ...

  • event-loop的microtask和macrotask

    有一个常见的面试题,相信很多人都看过。 会输出5个5,虽然写了setTimeout,但其实,里面的function...

  • 聊聊--宏任务和微任务、同步和异步

    最近几天,面试了几个应聘的。有一道面试题,答案真是千奇百怪。就是这么一道题: setTimeout(() => {...

  • 关于setTimeout和setInterval

    1、setTimeout定时器 setTimeout(code,millisec) setTimeout() 方法...

网友评论

      本文标题:setTimeOut面试题

      本文链接:https://www.haomeiwen.com/subject/qagyyftx.html