作用域链和闭包,图例详解那道setTimeout与循环闭

2019-10-23 01:47 来源:未知

图例详解那道setTimeout与巡回闭包的经文面试题

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

原稿出处爬山涉水 波同学   

图片 1

配图与本文非亲非故

我在详细图解功用域链与闭包一文中的结尾留下了叁个关于setTimeout与巡回闭包的思量题。

使用闭包,改良上面包车型大巴代码,让循环输出的结果依次为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 );
}

值得欢畅鼓励的是无数敌人在读了稿子之后确实对闭包有了尤其浓重的垂询,并准确的付出了二种写法。一些相恋的人能够认真的读书笔者的小说同一时候三个事例二个事例的侧边演习,这种承认对自个儿来讲实在要命震惊。但是也可能有豆蔻梢头部分基础稍差的仇人在阅读了后来,对于那题的敞亮照旧以为纳闷,因而应一些读者老爷的渴求,借此小说特地对setTimeout进行一个相关的知识分享,愿大家读完今后都能够有新的获得。

在前期学习setTimeout的时候,大家比较轻松掌握setTimeout有多个参数,第一个参数为二个函数,我们透过该函数定义就要推行的操作。第贰个参数为叁个时光飞秒数,表示延迟试行的岁月。

set提姆eout(function() { console.log('后生可畏分钟之后小编将被打印出来') }, 1000)

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

图片 2

上例执行结果

唯恐过四人对此setTimeout的精晓止步于此,但照旧有比超多个人发觉了有些别的的东西,并在说长道短里提出了疑义。举个例子上海体育场所中的那个数字7,是何等?

每贰个setTimeout在实行时,会重回一个唯意气风发ID,上图中的数字7,正是这些唯蒸蒸日上ID。我们在行使时,平常会使用三个变量将那几个唯意气风发ID保存起来,用以传入clearTimeout,消灭放大计时器。

var timer = setTimeout(function() { console.log('就算不清除小编,作者将会后生可畏秒以往出现。'); }, 1000) clearTimeout(timer); // 扑灭之后,通过setTimeout定义的操作并不会实践

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

接下去,大家还要求记挂别的叁个根本的标题,那正是set提姆eout中定义的操作,在怎么时候施行?为了引起我们的钟情,我们来看看下边包车型地铁事例。

var timer = setTimeout(function() { console.log('setTimeout actions.'); }, 0); console.log('other actions.'); // 思虑一下,当自家将setTimeout的延迟时间设置为0时,上边的实行顺序会是什么样?

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

在浏览器中的console中运作试试看,十分轻便就能够见道答案,如若您从未打中答案,那么作者那篇作品就值得你点一个赞了,因为接下去本人分享的小知识,大概会在笔试中国救亡剧团你一命。

在对于施行上下文的牵线中,作者与我们享受了函数调用栈这种非常数据结构的调用性子。在此边,将会介绍其余四个奇特的队列协会,页面中有着由setTimeout定义的操作,都将身处同三个类别中相继实行。

作者用下图跟大家来得一下行列数据结构的特征。

图片 3

队列跋山涉水的近义词先进先出

而以此队列实施的岁月,须求等待到函数调用栈清空之后才带头实行。即具备可进行代码施行完毕之后,才会起来奉行由setTimeout定义的操作。而那些操作踏向队列的逐后生可畏,则由设定的延迟时间来支配。

就此在地点那一个例子中,固然大家将延迟时间设置为0,它定义的操作照旧要求等待全数代码试行达成之后才开头实行。这里的延迟时间,实际不是相对于setTimeout试行这一刻,而是相对于其余代码试行完成那意气风发阵子。所以地方的例子实施结果就特别轻巧领会了。

为了援救大家知晓,再来二个结合变量提高的更加的复杂的事例。假若你能够科学看出施行各类,那么您对此函数的实施就有了相比较不错的认知了,若是还无法,就回过头去拜会别的几篇文章。

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,关于setTimeout就权且先介绍到这里,大家回过头来看看那些循环闭包的思索题。

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个setTimeout操作。而当这一个操作起来施行时,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值

当然,也足以在setTimeout的首先个参数处采纳闭包。

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

1、先明了一下功效域

倘诺大家起头化三个变量,举例跋山涉水的近义词var a = 1;参加这段代码执行的多少个剧中人物满含爬山涉水

内燃机跋山涉水的近义词原原本本担负整个JavaScript程序的编写翻译和举行

编写翻译器爬山涉水负担词法解析、语法剖析及代码生成等职务

作用域跋山涉水的近义词担负征集并维护由具备宣称的标记符(变量)组成的后生可畏多元查询,并实施大器晚成套特别严苛的平整,鲜明当前施行的代码对那个标志符的拜会权限

对于var a = 1;这段程序,引擎以为这里有多个精光两样的表明,多个在编译器编写翻译时管理,另八个在电动机械运输营时管理。

首先编写翻译器会将这段程序分解为词法单元,然后将词法单元分析成二个树结构,在代码生成阶段实行如下管理跋山涉水的近义词

1.遇上var a,编写翻译器会先理解效能域中是或不是早就存在该名称的变量,借使是,会忽视该申明延续编写翻译;假若否,会须求成效域在眼下功能域集结中宣示八个名称叫a的变量。

2.自此编写翻译器会为引擎生成在运营时要求的代码,这么些代码用来管理a = 2那些赋值操作。引擎运维时先问功能域是不是有改观量,要是有则运用,若无,则向上一级功用域中寻找。

假定引擎最终找到了a,就把1赋值给它,若无,就能抛出特别。

总括跋山涉水的近义词变量的赋值操作会实行三个动作,首先编写翻译器会在时下作用域中宣称三个变量,然后在运维时引擎会招来该变量,要是有则对它赋值。

功效域是依据名称查找变量的旭日东升套准则,而效率域链是那套准则的现实贯彻

2、作用域链

成效域链在实践上下文的创导阶段生成,是由最近条件以致上层情状的意气风发多级变量对象组成。它的功能是保证对施行境况有权访谈的全体变量和函数的安如泰山访谈。

标志符的分析是顺着成效域链一流超级进步查找效率域的长河,查找始终从效用域早先,找到则停止,否则一贯向上查找,知道全局成效域,即效率域链的末段。

经过贰个例证精通一下跋山涉水的近义词

var color = "blur";

function changeColor() {

    var anotherColor = "red";

    function swapColor() {   

        var tempColor = anotherColor;

        anotherColor = color;

        color = tempColor;

    }

}

上述代码共涉嫌三个推行蒙受爬山涉水全局意况、changeColor的龙精虎猛对情况和swapColor的一些意况。通过图来显示效果域链跋山涉水的近义词

图片 7

里面条件足以由此功效域链访问具备外界情形中的变量和函数,可是外界景况无法访谈内部条件。

闭包跟功效域链荣辱与共,下边就来介绍一下闭包。

3、闭包

闭包的概念爬山涉水当函数能够记住并拜见所在的功能域(全局成效域除此之外)时,就产生了闭包,尽管函数是在现阶段功能域之外施行的。轻松的话,正是叁个函数中又声称了三个函数,就生出了闭包。

function changeColor() {

    var anotherColor = "red";

    function swapColor() {

        console.log(anotherColor);

    }

    return swapColor;

}

var fn = changeColor();

这么代码推行时,就把swapColor的援用复制给了全局变量fn,而函数的实行上下文,在试行完毕生命周期甘休之后,实行上下文就能失掉引用,进而其占有的内存空间被垃圾回笼器释放。可是闭包的留存,打破了这种现象,因为swapColor的引用并未有被假释。所以闭包相当的轻易产生内部存储器泄漏的难题。

咋样让上边包车型客车代码输出1,2,3,4,5

for(vari=1;i<=5;i++){

setTimeout(functiontimer(){

console.log(i);

},0);

}

  1. 运用个中变量承袭一下

function fn(i) {

console.log(i);

}

for (var i=1; i<=5; i++) {

setTimeout( fn(i), 0 );

}

通过传播实参缓存循环的数据,况兼setTimeout的率先个参数是即时执行的函数,不实施不能。

2、使用即时实践函数

for (var i=1; i<=5; i++) {

setTimeout( (function timer() {

console.log(i);

})(), 0 );

}

3、用let或const声明

for (let i=1; i<=5; i++) {

setTimeout( function timer() {

console.log(i);

}, 0 );

}

那一个难题的关键缘由是因为实践到setTimeOut时函数未有实行,而是把它放到了职分队列中,等到for循环甘休后再推行。所以i最终都改为了5。

巡回中的事件也有这一个标题,因为事件须求接触,大超级多时候事件触发的时候循环已经实行完了,所以循环相关的变量就改成了最后一遍的值。

TAG标签:
版权声明:本文由金沙澳门唯一官网发布于前端开发,转载请注明出处:作用域链和闭包,图例详解那道setTimeout与循环闭