一道关于异步、作用域与闭包的面试题.md

5人阅读 评论(0) 收藏 举报
分类:

题目如下:

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


此题考查到了闭包?
为了很好的说明白这个事情,我们把它放到一个函数中:

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

上面的函数跟我们常见另一个例子(div绑定事件)有什么区别:

var fn = function() {
    var divs = document.querySelectorAll('div');
    for (var i = 0; i < 3; i++) {
        divs[i].onclick = function() {
            alert(i);
        };
    }
};
fn();

点击每个div都会弹出3。道理是一样的。因为alert(i)中的ifn作用越中的,因而这是闭包。
《javascript忍者秘籍》书里把一个函数能调用全局变量,也称闭包。
因为作者认为全局环境也可以想象成一个大的顶级函数。
怎么保证能弹出0,1, 2呢。
解决之道:以毒攻毒!
再创建个闭包!!

var fn = function() {
    var divs = document.querySelectorAll('div');
    for (var i = 0; i < 3; i++) {
        divs[i].onclick = (function(i) {
            return function() {
                alert(i);
            };
        })(i);
    }
};
fn();

或者如下的写法:

var fn = function() {
    var divs = document.querySelectorAll('div');
    for (var i = 0; i < 3; i++) {
        (function(i) {
            divs[i].onclick = function() {
                alert(i);
            };
        })(i);
    }
};
fn();

因此原题如果也想setTimeout也弹出0,1,2的话,改成如下:

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

转自 https://www.qdfuns.com/article/17398/e8a1ce8f863e8b5abb530069b388a158.html

查看评论

关于项目干系人

-
  • 1970年01月01日 08:00

闭包经典面试题

闭包应该是前段面试中经常碰到的面试题,很多人都会在这个问题上被问住。如果想要弄清楚就要掌握闭包的概念; 首先看面试题: for (var i = 1; i   setTimeout...
  • donggx
  • donggx
  • 2017-04-14 14:13:28
  • 8599

最新今日头条面试题目(js闭包、原型、继承、作用域)(2016)

考察js中比较核心的几个知识点,闭包、原型链、继承、作用域
  • u014306354
  • u014306354
  • 2016-09-30 11:12:45
  • 3705

javascript 闭包,this,作用域,综合应用面试题

var name = "The Window";  var object = {     name : "My Object",    getNameFunc : function(){       ...
  • qiu265843468
  • qiu265843468
  • 2017-04-20 11:30:53
  • 371

闭包--面试之必问

要说闭包,大家肯定不陌生。但是,加入被问到具体的概念和怎么用,就有可能概念不太清晰。下面做个简单总结。 -----------------------------------------------...
  • qq_29066959
  • qq_29066959
  • 2016-03-04 16:17:21
  • 1550

javascript理解之变量作用域与闭包

何为闭包闭包是指能够访问自由变量的函数 (变量在本地使用,但在闭包中定义)。换句话说,定义在闭包中的函数可以“记忆”它被创建时候的环境。函数作用域与声明提前var scope= "global sco...
  • sinat_25127047
  • sinat_25127047
  • 2016-06-12 17:33:11
  • 1579

JavaScript概念总结:作用域、闭包、对象与原型链

1 JavaScript变量作用域 1.1 函数作用域 没有块作用域:即作用域不是以{}包围的,其作用域完成由函数来决定,因而if /for等语句中的花括号不是独立的作用域。 如前述,JS的在函...
  • zzulp
  • zzulp
  • 2012-11-03 20:49:13
  • 18374

关于闭包的面试题引发的思考

闭包一直想写个闭包的总结,无奈自己理解的太渣,不敢乱写,最后还是想写一点东西。 关于闭包,在我目前学习的理解中,就是闭包它是会驻留在内存中,不会被js垃圾回收机制清除,因此它也是模拟的全局变量,也就...
  • qq_36008914
  • qq_36008914
  • 2017-03-19 23:05:01
  • 1013

js作用域与原型的笔试题

作用域与原型
  • datou0529
  • datou0529
  • 2016-09-20 20:43:02
  • 1938

关于js作用域,闭包的小例子(经典js)

js经典例子: js闭包的好处之一:隔开js的作用域,避免变量对全局的污染(影响) 现在有如下需求: html中有几个选项卡 点击不同的选项卡,弹出当前选项卡的索引值 或许你认为这很简单,但是:1...
  • adley_app
  • adley_app
  • 2017-11-05 11:31:38
  • 124
    个人资料
    持之以恒
    等级:
    访问量: 2017
    积分: 158
    排名: 110万+
    文章存档