您的位置:首页 > 职场人生

一道JS面试题

2016-06-22 17:18 330 查看
原文地址:http://www.w3cfuns.com/notes/17398/e8a1ce8f863e8b5abb530069b388a158.html  侵删

题如下:

for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
}
结果是:0 1 2 3 3 3
很多公司面试都爱出这道题,此题考察的知识点还是蛮多的。

为了防止初学者栽在此问题上,此文稍微分析一下。

都考察了那些知识点呢?

异步、作用域、闭包,你没听错,是闭包。

我们来简化此题:

setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
先打印2,后打印1。

因为是setTimeout是异步的。

正确的理解setTimeout的方式(注册事件):

有两个参数,第一个参数是函数,第二参数是时间值。

调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用。

没啥不好理解的。就像我们给按钮绑定事件一样:
btn.onclick = function() {
alert(1);
};
这么写完,会弹出1吗。不会!!只是绑定事件而已!

必须等我们去触发事件,比如去点击这个按钮,才会弹出1。

setTimeout也是这样的!只是绑定事件,等主程序运行完毕后,再去调用。

setTimeout的时间值是怎么回事呢?

比如:
setTimeout(fn, 2000)
我们可以理解为2000之后,再放入事件队列中,如果此时队列为空,那么就直接调用fn。如果前面还有其他的事件,那就等待。

因此setTimeout是一个约会从来都不准时的童鞋。

继续看:
setTimeout(function() {
console.log(i);
}, 0);
var i = 1;
程序会不会报错?

不会!而且还会准确得打印1。

为什么?

因为真正去执行console.log(i)这句代码时,var i = 1已经执行完毕了!

所以我们进行dom操作。可以先绑定事件,然后再去写其他逻辑。
window.onload = function() {
fn();
}
var fn = function() {
alert('hello')
};
这么写,完全是可以的。因为异步!

es5中是没有块级作用域的

for (var i = 0; i < 3; i++) {}
console.log(i);
也就说i可以在for循环体外访问到。所以是没有块级作用域。

但此问题在es6里终结了,因为es6,发明了let。
这回我们再来看看原题。

原题使用了for循环。循环的本质是干嘛的?

是为了方便我们程序员,少写重复代码。

让我们倒退50年,原题等价于:

var i = 0;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
因为setTimeout是注册事件。根据前面的讨论,可以都放在后面。

原题又等价于如下的写法:
var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
这回你明白了为啥结果是0 1 2 3 3 3了吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: