最近看到一条有意思的闭包面试题,可是看到原文的解析,我本身以为有点迷糊,因此本身从新作一下这条题目。html
function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; } // 第一个例子 var a = fun(0); // 返回undefined a.fun(1); // 返回 ? a.fun(2); // 返回 ? a.fun(3); // 返回 ? // 第二个例子 var b = fun(0) .fun(1) .fun(2) .fun(3); //undefined,?,?,? // 第三个例子 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined,?,?,?
先大体说一下这个函数的执行过程:面试
① 初始化一个具名函数,具名函数就是有名字的函数,名字叫 fun。闭包
② 第一个 fun 具名函数执行以后会返回一个对象字面量表达式,即返回一个新的object对象。函数
{ // 这是一个对象,这是对象字面量表达式建立对象的写法,例如{a:11,b:22} fun: function(m) { return fun(m, n); } }
③ 返回的对象里面含有fun这个属性,而且这个属性里面存放的是一个新建立匿名函数表达式function(m) {}
。code
④ 在③里面建立的匿名函数会返回一个叫 fun 的具名函数return fun(m, n);
,这里须要说明一下这个 fun 函数返回以后的执行过程:htm
1. 返回 fun 函数,但默认不执行,由于在 js 里面,函数是能够保存在变量里面的。 2. 若是想要执行 fun 函数,那么首先会在当前做用域寻找叫fun 名字的具名函数,可是由于当前做用域里 fun 名字的函数是没有被定义的,因此会自动往上一级查找。 2.1 注解:当前的做用域里是一个新建立的对象,而且对象里面只有 fun 属性,而没有 fun 具名函数 2.2 注解:js 做用域链的问题,会致使他会不断地往上级链查找。 3. 在当前做用域没找到,因此一直往上层找,直到找到了顶层的 fun函数,而后执行这个顶层的 fun 函数。 4. 而后这两个 fun 函数会造成闭包,第二个 fun 函数会不断引用第一个 fun 函数,从而致使一些局部变量例如 n,o 得以保存。
所谓闭包:各类解释都有,但都不是很接地气,简单的来讲就是在 js 里面,有一些变量(内存)能够被不断的引用,致使了变量(内存)没有被释放和回收,从而造成了一个独立的存在,这里涉及了js 的做用域链和 js 回收机制,结合二者来理解就能够了。
var a = fun(0); // 返回 undefined
注解:对象
fun(n, o)
是有2个参数的,若是第二个参数没有传,那么默认就会被转换为 undefined,因此执行以后输出 undefined,由于 console.log 输出的是o console.log(o);
。fun(m, n);
。function fun(n, o) { // ① console.log(o); // 这里首先输出了 n 的值为undefined return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; }
a.fun(1); // 返回 0
注解:blog
var a = fun(0);
,返回了一个对象,而且赋值给了变量a,因此 a 是能够访问对象里面的属性的,例如a.fun
。a.fun(1);
这里意思是:ip
()
。a.fun()
实际上调用的是 fun 属性里面的匿名函数,因为匿名函数返回的fun(m, n);
没法在当前做用域找到(由于当前做用域没有这个定义这个函数),因此会往上找,找到了顶层的函数fun(n, o)
,这样就会出现闭包的状态,顶层的fun 函数被内层的 fun 函数引用,以前①的fun(0)
的0被保存下来了,做为 n 参数的值。a.fun(1)
这里传入了第一个参数1,因此就是 m=1,(由于③接收一个参数)。fun(m,n)
就会是fun(1,0)
,因此输出0// 已经执行过一次var a = fun(0) function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ m=1 return fun(m, n); // ④ 不断引用①,闭包生成,①的n 的值被保存为0 } }; }
a.fun(2); // 返回 0
注解:内存
a.fun(1);
是同样的流程执行。 fun(2,0)
执行,那么输出 o 就是0了function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) { // ③ return fun(m, n); // ④ } }; }
a.fun(3); // 返回 0
跟上面雷同,因此不作解释了。
第二个例子实际上是一个语句,只是进行了链式调用,因此会有一些不同的处理。
var b = fun(0) // 返回 undefined
注解:
fun(0).fun(1) // 返回 0
注解:
fun(0)
的时候返回了一个对象,对象里面有 fun 属性,而这个 fun 属性的值是一个匿名函数,这个匿名函数会返回一个 fun 函数。fun(0)
后,再链式直接执行.fun(1)
的时候,它是会调用前者返回的对象里的 fun 属性,而且传入了1做为第一个参数,即m=1,而且返回的 fun 函数跟前者造成闭包,会不断引用前者,因此 n=0 也被保存下来了。fun(m, n)
即 fun(1,0)
,因此返回0fun(0).fun(1).fun(2)
注解:
fun(0)
的时候返回了一个对象,对象里面有 fun 属性,而这个 fun 属性的值是一个匿名函数,这个匿名函数会返回一个 fun 函数。fun(0)
后,再链式直接执行.fun(1)
的时候,它是会调用前者返回的对象里的 fun 属性,而且传入了1做为第一个参数,即m=1,而且返回的 fun 函数跟前者造成闭包,会不断引用前者,因此 n=0 也被保存下来了。当再次链式直接执行.fun(2)
的时候,这里使用的闭包是.fun(1)
返回的闭包,由于每次执行 fun 函数都会返回一个新对象,而.fun(2)
引用的是.fun(1)
,因此 n 的值被保留为1
.fun(2)
返回的是fun(m, n)
,而这里会跟.fun(1)
(即fun(1, o)
)造成闭包,因此1为 n 的值被保留。.fun(2)
找到.fun(1)
就立刻中止向上搜索了,因此引用的是.fun(1)
的值。跟第三个返回相似,因此不作解释了。
// 这里已经无需多说了,跟第二个例子相似。 var c = fun(0).fun(1); // 返回 undefined 和0
c.fun(2); // 第三个返回 1 c.fun(3); // 第四个返回 1
注解:
为了不原文被吃掉,因此我这里保留了截图,而且加了一篇解释 js 闭包还不错的文章做为参考使用。