for循环中引用setTimeout定时器

这种情况一般会出现在面试题中,考验基本功扎不扎实!

请看题1:

填空题,要求输出 a 的所有项。

var a=[1,2,3];
var len=a.length;
for(___){
    setTimeout{function(){
        console.log(__);
    },0}
}

该题目考察的是: JavaScript 的单线程以及 setTimeout 的异步特性。

【注】:JavaScript 引擎是单线程运行的,浏览器运行期间只有一个线程在运行 js 程序。浏览器的内核是多线程的,他们在内核控制下相互配合,以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

JavaScript 引擎:是基于事件驱动单线程执行的,JavaScript 引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个 JavaScript  线程在运行 JavaScript 程序。

GUI 渲染线程:负责浏览器界面的渲染,当界面需要重绘的时候或者由于某种操作引发回流时,该线程就会执行,需要注意 GUI 渲染线程与 JavaScript 引擎是互斥的,当 JavaScript 引擎执行的时候 GUI 线程会被挂起,GUI 更新会被保存在一个队列中,等到 JavaScript 引擎空闲时才立即被执行。

浏览器事件触发线程:当一个事件被触发时该线程会把事件添加到待处理的队列末尾,等待 JavaScript 引擎的处理。这些事件可来自 JavaScript 引擎当前执行代码块,如 setTimeout,也可来自浏览器内核的其他线程如鼠标点击、ajax 异步请求等,但由于 JavaScript 的单线程关系所有这些事件都得排队等待 JavaScript 引擎处理。

当线程中没有任何同步代码的前提下才会执行异步代码。

在上题中 setTimeout 是异步的代码,即使 setTimeout 中设置的等待时间为 0 也不会立刻执行,for 循环代码是同步,所以要等待 for 循环执行完以后才会执行 setTimeout。因此此时如果使用传统的 for 循环,在 setTimeout 中打印 a[i] 的话会出现 undefined 的情况。此时可以在 for 循环外定义一个局部变量 j。打印的时候打印 a[j++] 就可以实现a数组的遍历。

示例代码:

定义一个自增变量 j

var a=[1,2,3,4];
var i,len=a.length;
var j=0;
for(i = 0; i < len; i++) {
    setTimeout(function() {
        console.log(a[j++]);
    }, 0);
}

或者:

在定时器外面包层函数

for(i = 0; i < len; i++) {
    (function(i) {
        setTimeout(function() {
	    console.log(a[i]);
	}, 0);
    })(i)
}

请看题2:

要求输出:1 2 3

for(var i = 1; i <= 3; i++) {
    setTimeout(function() {
        console.log(i)
    }, 0)
}
// 4,4, 4

上面这个代码块会打印三个 `4` 出来,而我们预想的结果是打印 1 2 3 。

之所以会这样,是因为 setTimeout 中的 i 是对外层 i 的引用;

当 setTimeout 的代码被解释的时候,运行时只是记录了 i 的引用,而不是值;

而当 setTimeout 被触发时,三个 setTimeout 中的 i 同时被取值,由于它们都指向了外层的同一个 i,而那个 i 的值在迭代完成时为 4,所以打印了三次 `4`。

为了得到我们预想的结果,我们可以把 i 赋值成一个局部的变量,从而摆脱外层迭代的影响。

像这样:

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

或者这样:

定义一个自增变量

var j=0;
for(i = 0; i <=3; i++) {
    (function(i) {
        setTimeout(function() {
	    console.log(a[j++]);
	}, 0);
    })(i)
}		



原文链接:HelloWeb前端网 » for循环中引用setTimeout定时器 » 感谢您的浏览,希望能有所帮助。

欢迎您加入“Helloweb” 学习交流群:HelloWeb-学习交流群 196291215 共同交流并结识同行,在这里说出您的收获与感想或有什么不同的观点,我们期待您的留言,分享,让我们一起进步!

喜欢 ()or分享