菜单

一个经典的闭包面试题

2018年12月18日 - JavaScript

祈求例详解这道set提姆eout与巡回闭包的经典给试题

2017/03/06 · JavaScript
· 1 评论 ·
settimeout,
闭包

初稿出处: 波同学   

图片 1

配图与本文无关

我在详见图解成效域链与闭包无异于温和遭受的末梢留下了一个有关set提姆(Tim)eout与循环闭包的思考题。

动用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5

JavaScript

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

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

值得心潮澎湃之是不少爱人于宣读了章之后确实对闭包有矣尤其深远的了然,并精确的被起了两种写法。一些仇敌会认真的看我之稿子同时一个例证一个事例的左训练,那种认可对自而言实在坏震撼。不过呢来部分基础稍差之情侣以翻阅了后,对于那题的了解如故感觉到疑惑,由此应一些读者老爷的渴求,借那一个小说特别针对set提姆(Tim)eout举行一个相关的知识分享,愿大家诵读毕将来仍可以起新的得到。

于初期学习set提姆eout的时刻,我们挺爱懂set提姆eout有一定量个参数,第一单参数为一个函数,我们通过该函数定义将要执行之操作。第二独参数为一个时皮秒数,表示延迟执行之时日。

set提姆(Tim)eout(function() { console.log(‘一分钟后我将于打印出’) }, 1000)

1
2
3
setTimeout(function() {
    console.log(‘一秒钟之后我将被打印出来’)
}, 1000)

图片 2

上例执行结果

兴许过多口对set提姆(Tim)eout的知止步于斯,但要么有过多丁意识了片别样的东西,并在评论里提议了问题。比如达图中之那数字7,是呀?

诸一个set提姆eout在推行时,会回来一个唯一ID,上图被之数字7,就是其一唯一ID。我们在运时,平日会下一个变量将是唯一ID保存起来,用以传入clear提姆eout,清除定时器。

var timer = set提姆eout(function() {
console.log(‘倘使无散我,我将会面雷同秒将来现身。’); }, 1000)
clear提姆(Tim)eout(timer); // 清除后,通过set提姆(Tim)eout定义的操作并无碰面进行

1
2
3
4
5
var timer = setTimeout(function() {
    console.log(‘如果不清除我,我将会一秒之后出现。’);
}, 1000)
 
clearTimeout(timer);  // 清除之后,通过setTimeout定义的操作并不会执行

接通下,我们还欲考虑此外一个关键之问题,这就是setTimeout中定义的操作,在什么时候实施?为了唤起我们之厚,大家来探视上边的例证。

var timer = set提姆eout(function() { console.log(‘set提姆eout actions.’);
}, 0); console.log(‘other actions.’); //
思考一下,当我将set提姆eout的延迟时间设置为0时,上边的履行各个会是什么?

1
2
3
4
5
6
7
var timer = setTimeout(function() {
    console.log(‘setTimeout actions.’);
}, 0);
 
console.log(‘other actions.’);
 
// 思考一下,当我将setTimeout的延迟时间设置为0时,上面的执行顺序会是什么?

于浏览器中之console中运作试试看,很爱就会解答案,假如您无命中答案,那么我登时首文章就是值得你点一个赞了,因为属下自己享受的略微知识,可能汇合当笔试中抢救你一命。

在对于施行上下文的牵线着,我与我们大饱眼福了函数调用栈这种特有数据结构的调用特性。在那边,将谋面介绍此外一个奇特的队列社团,页面被具备由set提姆eout定义的操作,都用放在同一个列中各样执行。

本身用生图与我们来得一下阵数据结构的性状。

图片 3

队:先进先出

苟以此行列执行的时间,需要等到函数调用栈清空之后才起来执行。哪怕享可实施代码执行完毕之后,才会见初始进行由set提姆eout定义之操作。而这一个操作上队列的各种,则由于设定的延迟时间来决定。

从而当点是例子中,即便大家以延迟时间设置为0,它定义之操作依然要拭目以待所有代码执行完毕后才起履行。这里的延迟时间,并非相对于set提姆eout执行及时一刻,而是绝对于其他代码执行完毕这一阵子。所以地点的例证执行结果虽非常容易领悟了。

以帮大家了解,再来一个结合变量升高的逾扑朔迷离的例证。假诺您可知正确看出执行顺序,那么你对函数的实施就暴发矣较不错的认识了,尽管还无可以,就转头喽头去看其他几首作品。

setTimeout(function() { console.log(a); }, 0); var a = 10;
console.log(b); console.log(fn); var b = 20; function fn() {
setTimeout(function() { console.log(‘setTImeout 10ms.’); }, 10); }
fn.toString = function() { return 30; } console.log(fn);
setTimeout(function() { console.log(‘setTimeout 20ms.’); }, 20); fn();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
setTimeout(function() {
    console.log(a);
}, 0);
 
var a = 10;
 
console.log(b);
console.log(fn);
 
var b = 20;
 
function fn() {
    setTimeout(function() {
        console.log(‘setTImeout 10ms.’);
    }, 10);
}
 
fn.toString = function() {
    return 30;
}
 
console.log(fn);
 
setTimeout(function() {
    console.log(‘setTimeout 20ms.’);
}, 20);
 
fn();

图片 4

上栗执行结果

OK,关于set提姆eout就暂先介绍及此处,大家回过头来看看好循环闭包的思考题。

JavaScript

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

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

如若我们一直那样勾画,遵照set提姆(Tim)eout定义之操作以函数调用栈清空之后才会师履之特点,for循环里定义了5只set提姆(Tim)eout操作。而当这些操作起来履行时,for循环的i值,已经先期一步变成了6。由此输出结果总为6。而我辈记念要于输出结果依次执行,大家便亟须依赖闭包的性状,每一回循环时,将i值保存于一个闭包中,当set提姆eout中定义的操作实施时,则做客对诺闭包保存之i值即可。

使我辈知道当函数中闭包判定的规则,即进行时是否当中间定义之函数中做客了上层成效域的变量。由此大家要包裹一层自举行函数为闭包的形成提供规范。

故此,大家就待2独操作就得成功问题要求,一凡是行使于举行函数提供闭包条件,二凡流传i值并保存于闭包中。

JavaScript

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

1
2
3
4
5
6
7
8
for (var i=1; i<=5; i++) {
 
    (function(i) {
        setTimeout( function timer() {
            console.log(i);
        }, i*1000 );
    })(i)
}

图片 5

选用断点调试,在chrome中翻执行各种及诸一个闭包中不同的i值

当,也堪当set提姆eout的第一个参数处用闭包。

JavaScript

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

1
2
3
4
5
6
7
for (var i=1; i<=5; i++) {
    setTimeout( (function(i) {
        return function() {
            console.log(i);
        }
    })(i), i*1000 );
}

1 赞 6 收藏 1
评论

图片 6

Shut up, show me the code!!!

function showBiBao() {
    for (var i = 0; i < 5; i++) {
      setTimeout( function timer() {
          console.log(i);
      }, 1000 );
    }
    console.log(i)
}
// 会输出什么
showBiBao()

会见用到之个别只知识点:

提示:
此代码是生bug的,怎么化解?

解决办法:

第一步:先找出bug原因
1.1:for循环5不行,那么当设定了5个定时器,这个对
1.2:set提姆eout等待for循环执行好后立时调用定时器
1.3:set提姆(Tim)eout被在了行的数据结构中(for循环),等待上下文的代码运行后再度实施定时器,此时运行定时器,变量i已经改成了5(此时5个定时器的i都是5),所以输出都是5

第二步:怎么解决?
2.1:需要拿每个定时器访问的变量独立起来,改变i的效能域
2.1:能够就此闭包实现这多少个目标:在for循环里描写一个闭包
2.3:show code

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

其三步:还会怎么开?
3.1:改变i的效能域就可以清除bug
3.2:可以为此let表明一个只是针对现阶段{}(块效用域)内中之变量。

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

useLetChange()

相关文章

发表评论

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

网站地图xml地图