一道闭包题题解
题目来自 大部分人都会做错的经典JS闭包面试题。
很有意思的题目,切一发。
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
//问:三行a,b,c的输出分别是什么?
先看第一组执行,fun(0) 后首先打印 undefined,没有问题。之后变量 a 便被赋值为 fun 函数所 return 的对象。即:
a = {
fun: function(m) {
return fun(m, n);
}
};
这里要重点注意的是参数 n,值为 0,这就是闭包和作用域链。
接着执行 a.fun(1) a.fun(2) a.fun(3),我们以 a.fun(1) 举例。a.fun(1) 的执行结果,因为没有赋值(其实有个 return value),所以其实就是执行了一遍 fun(m, n),上面说了,n 值为 0,所以控制台输出为 0。后两个输出同理。这里要注意的就是这个 n,因为作用域链,所以 n 能获取值,为 0,因为 n 被变量 a 所引用,所以它一直贮藏在内存中。
再来看第二组,我们可以把它改成这样。
var a = fun(0);
var b = a.fun(1);
var c = b.fun(2);
var d = c.fun(3);
第一行,打印 undefined,没有问题,a 返回对象,然后执行 a.fun(1),打印 0,这些跟第一次的执行相同。a.fun(1),其实就是执行 fun(m, n),其实就是 fun(1, 0),return 的对象赋值给 b。
var b = {
fun: function(m) {
return fun(m, n); // n=1
}
};
类似的结果,唯一不同的是 n 的值变了,这是由 fun() 传入的参数所决定的。
接下去,b.fun(2),执行 fun(2, 1),打印出 1,然后将 return 的对象赋值给 c。
var c = {
fun: function(m) {
return fun(m, n); // n=2
}
};
最后一步也是类似,所以依次打印 undefined, 0, 1, 2。
最后看第三组。由第二组可得变量 c 的结果:
var c = {
fun: function(m) {
return fun(m, n); // n=1
}
};
然后执行过程和 1 类似,就不用多说了。
综:个人认为这道题的 "恶心" 之处多数在于函数中调用函数本身(fun 函数中调用 fun 函数),而引起的思路混乱,其他部分其实跟下面代码类似,归根结底就是被引用的变量会始终存在在内存中。
function fn() {
var num = 0;
return function() {
console.log(num++);
}
}
var a = fn();
a(); // 0
a(); // 1
var b = fn();
b(); // 0
b(); // 1
更多精彩分析可以参考原文。