菜单

一个经的闭包面试题

2018年12月20日 - JavaScript

manbetx网页手机登录版,祈求例详解这道set提姆(Tim)eout与巡回闭包的经典给试题

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

初稿出处: 波同学   

manbetx网页手机登录版 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提姆eout举行一个连锁的知识分享,愿我们诵读了之后都能暴发新的获得。

以初学习set提姆eout的下,我们老轻精晓set提姆(Tim)eout有点儿单参数,第一单参数为一个函数,大家经过该函数定义将要执行的操作。第二独参数为一个时空皮秒数,表示延迟执行之年月。

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

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

manbetx网页手机登录版 2

上例执行结果

兴许多口对set提姆(Tim)eout的敞亮止步于斯,但要么出成百上千丁发现了一些任何的东西,并在评论里指出了疑问。比如达图中之这数字7,是呀?

各国一个set提姆(Tim)eout在实践时,会回去一个唯一ID,上图被的数字7,就是这么些唯一ID。我们在用时,平日会用一个变量将是唯一ID保存起来,用以传入clear提姆(Tim)eout,清除定时器。

var timer = set提姆eout(function() {
console.log(‘要是非破我,我拿会一如既往秒后出现。’); }, 1000)
clear提姆eout(timer); // 清除后,通过set提姆eout定义之操作并无会晤举办

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

对接下,我们尚待考虑此外一个至关首要的题目,这便是set提姆(Tim)eout中定义之操作,在何时实施?为了引起我们之敬重,我们来看望下边的例证。

var timer = set提姆(Tim)eout(function() { console.log(‘set提姆(Tim)eout actions.’);
}, 0); console.log(‘other actions.’); //
思考一下,当我将set提姆(Tim)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定义的操作,都用身处同一个系列中各种执行。

自由此生图跟我们来得一下列数据结构的特点。

manbetx网页手机登录版 3

队:先进先出

假定这个行列执行的时光,需要等到函数调用栈清空之后才起来实施。虽然享可实施代码执行完毕之后,才会起来履行由set提姆(Tim)eout定义之操作。而那么些操作上队列的顺序,则由设定的延迟时间来决定。

故当点这多少个事例中,即便我们以延迟时间设置为0,它定义之操作依旧要等待所有代码执行完毕之后才起实施。这里的延迟时间,并非相对于set提姆(Tim)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();

manbetx网页手机登录版 4

上栗执行结果

OK,关于set提姆(Tim)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提姆eout定义的操作以函数调用栈清空之后才会见实施之特征,for循环里定义了5只set提姆eout操作。而当这一个操作起来推行时,for循环的i值,已经先行一步变成了6。由此输出结果总为6。而我辈回忆要于输出结果依次执行,我们便不可以不依闭包的表征,每一次循环时,将i值保存于一个闭包中,当set提姆(Tim)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)
}

manbetx网页手机登录版 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
评论

manbetx网页手机登录版 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提姆(Tim)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地图