用一道面试题考察对闭包的理解
关于闭包的用法,几乎是所有前端面试中必点的菜之一,也是考察javascript掌握程度的重要知识之一,下面这题,是某知名IT企业出的题型,我稍加修改,分享如下:
var name = 'global';
var obj = {
name : 'obj',
dose : function(){
this.name = 'dose';
return function(){
return this.name;
}
}
}
alert(obj.dose().call(this))
请写出执行结果?
关于这样的题型,应当怎样去分析呢?
obj.dose().call(this) 这个表达式有点长,看着有点眼晕,不妨进行一个等价变形。
var xxx = obj.dose();
xxx.call(this);
这样就清晰多了。这样一眼就看出是在考察call的用法和this的指向。稍有点基础的,一眼就可以看出此处的this就是window对象。如果你看不出,就再去看看那本红宝书3
即使知道此处的this是window还没完。还要顺路普及一下call的用途:
// 1. 替换函数运行环境中的this
// 2. 传递参数
// 3. 运行函数
通过前面的分析可以知道xxx是这样一个函数:
function(){
return this.name;
}
由于call指定了this是window,所以return this.name 就是 window中的name,即global;
如果是面试呢,这题到此就结束了,不过举一反三才是我的目的。因此,下面我稍改一下题目:
var name = 'global';
var obj = {
name : 'obj',
dose : function(){
this.name = 'dose';
return function(){
return this.name;
}.bind(this)
}
}
alert(obj.dose().call(this))
由于return的function中用了bind,所以相当于固定了this,外边再call什么进来,也只是碍眼法而已。由于函数内部邦定了this,所以此处的情况要另外分析了
首先,obj对象定义了name属性为'obj';接着在dose 方法内,又改写了name属性为'dose'; 根据作用域链的就近原则,alert访问的肯定就是'dose'这个值了。
然而ie派认为在return中用bind不常见,兼容性也不高。那不妨再变一下:
var name = 'global';
var obj = {
name : 'obj',
dose : function(){
var that = this;
this.name = 'dose';
return function(){
return that.name;
}
}
}
alert(obj.dose().call(this))
这种写法,自然大家都比较认同了。考察还是相同的内容,只不过是邦定this的手法不一样而已。与其说是考察闭包,不如说是考察对基础知识的理解,因为bind,call,apply之类的方法都是平时使用频率很高的,对它们多花点时间琢磨一下,必然是有好处的。
最后呢再分享一个面试的趣事。面试官问我,用闭包有什么好处?我balabla说了个一二三四。他接着又问我,那用闭包又有什么坏处?我又是balabala说了一二三四,他就笑了。说你这一边是矛,一边是盾,到底是矛好呢还是盾好呢? 当时也没想这么多,反射性的回答说,看情况选用咯。他说这样是不行的。
原来挖了个坑在这里等着我呢,真是太不厚道了。凡事都有两面性嘛,只要撑握的好,自然是可以避害用利。我们都知道电是很危险,也很有用,只要掌握了它的特性,就能很好的利用,而不是受其害,所以并不是矛盾的就不可取。
总结一点:学东西不可浅尝则止,一定要深入原理,举一反三。