一道闭包题题解

题目来自 大部分人都会做错的经典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

更多精彩分析可以参考原文。

原文地址:https://www.cnblogs.com/lessfish/p/5092997.html