今天看见一道自认为比较有难度比较绕的闭包题目,倒腾了好一会才弄出答案来,这题目是别人博客里面写的,本着“自己懂还不算真的懂,给别人讲懂了才算真的懂的原则”,故我在这篇博客里面来说说这道题目
这是本人第一次描述这些题目,如果有用词不当,请提出^_^
原作者的链接我会赋到最下面,如有冒犯请通知我,我会删掉的。
题目如下
1 | function fun(n,o) { |
是不是有点懵逼?如果是,请先冷静一下哈,太懵逼是没办法解决问题的(虽然我看到第一眼就懵了,但是你可以慢慢来,还是可以梳理好的)。如果不是那你一定在闭包上面有深深的理解。
第一个 fun 函数,是标准具名函数声明,返回了一个对象字面量表达式,也就是一个 object 。
第二个 fun 是一个属性,是匿名函数表达式。
第三个 fun 是调用的,也就是第一个fun,有点递归的意思。这里或许你会问为什么不等于第二个 fun ?其实我们可以将以下代码在浏览器console测试一下
假如是第三个fun是同第二个一样:1
2
3
4
5
6var o={
fn:function (){
console.log(fn);
}
};
o.fn();//ERROR报错
我们发现它最终会报错,显示fn is not defined(...)
下面再看另一个1
2
3
4var fn = function (){
console.log(fn);
};
fn();//function (){console.log(fn);};正确
这次就没有报错了,var 在函数外部创建了一个fn,函数内部找不到fn则可以向上寻找,但是第一种是在函数内部创建的,没办法找到。
所以我们可以得知,第三个函数是第一个的这个fun
所以我们回顾一下原来的函数
1.第一行
1 | var a = fun(0); a.fun(1); a.fun(2); a.fun(3); |
首先fun(0),即n = 0传入了fun(n,o),此时o没有值,则为undefined :
故第一个输出为 undefined
接着的return返回了一个对象字面量表达式,也就是类似下面1
2
3
4
5var a = {
fun:function(m){
return fun(m,n);
}
}
接着的 a.fun(1) 参数 m = 1 , n的值我们之前在fun(0)里面知道了——是 0。则下面的fun(m,n)则是 fun(1,0) ,再倒回去看函数第一个fun(最外面的那个)m->n , n->o,所以打印出来的o也就是最后n的值,为 0
那么 a.fun(2)呢?其实也是差不多的,m = 2的时候,n还是原来的 n = 0,你问为什么?因为在var a = fun(0)的时候已经确定了呢,所以最终输出 0(第三个fun的参数n将值赋给了第一个fun的o)
所以这么看来的话,第一行的第三个是不是也是很好解决呢?
和前面两个一样也是等于0
所以第一行的答案是:
undefined 0 0 0
2.第二行
1 | var b = fun(0).fun(1).fun(2).fun(3); |
- 首先第一个值是
undefined是同刚刚第一行的一样的。 - 那么接下来看又引用了
fun这个方法,也就是m = 1,n是原来的0,也就是最后o = 0,那么第二个输出的数字就为 0 - 此时还没完呢,接着又马上来了个
fun(2),此时的fun其实又变成了最外层的那个fun,那么m = 2,n = 1,对应fun(n,o)就是n=2,o=1.此刻你应该明白了,下一个输出的数是 1 - 最后一个是
fun(3),这个又再次变成了fun里面的那个方法fun里面的函数,由于上次n=2 m=3=>fun(3,2),最后一个输出 2
所以第二行的答案是:
undefined 0 1 2
3.第三行
1 | var c = fun(0).fun(1); c.fun(2); c.fun(3); |
- 由于前面两个在第二行里面是类似的,这边就直接跳过了,输出undefined 和0
- 这时候,
c.fun(2)=>fun(m,n)=>m = 2,n在上一个已经等于1, 也就是在fun(n,o)里面n =2,o = 1;
所以此次输出 1 - 第三个
c.fun(3)=>fun(m,n)=>m=3,n=1。所以最后在函数fun(n,o)里n=3,o = 1,所以还是输出1
所以第三行的答案是:
undefined 0 1 1