由于最近在学习关于闭包相关的知识,并且闭包这个知识点让我有点搞不太清楚其具体的定义,所以在网上也查阅了很多大佬的讲解和对闭包的一个定义。
最后感觉还是MDN上的说法感觉比较好理解一些,对闭包还是不太理解的道友可以尝试看一看。
MDN上是这样说的:闭包是函数和声明该函数的词法环境的组合
原地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
在了解闭包的过程中遇到一个很多地方都出现的一个面试题,按照自己的想法想了下发现几乎没对...所以就花了些时间分析了一下,供自己以后忘记了可以回顾一下。
同时,如果这里依旧存在一些误区,希望各位大佬们在评论区帮忙指正,感激万分!
原题如下:
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); // ? var b = fun(0).fun(1).fun(2).fun(3); // ? var c = fun(0).fun(1); // ? c.fun(2); // ? c.fun(3); // ?
有兴趣的道友们也可以试着先想一想答案,看下是否一致?
以下是解题思路:
1. 首先我对这个题画了下题目中两个fun函数中的一些信息 (可能不太完整,但是解题应该够用了)
2. 当执行 var a = fun(0); 时,内存中开辟了一块新的空间给这个新的对象,这个对象中定义了一个方法fun。并且在fun(0)执行时,在当前作用域下的变量信息如下:
因此,当该语句执行时,控制台打印的值为 undefined
3. 当执行 a.fun(1) 时,因为对象a中的fun方法在定义时所处的环境( [[scope]] )中存在一个变量n和变量o,
并且在这条语句执行的时候,变量n( 此时n的值为0) 被第三方 (除了函数fun和方法fun)引用了,也就是被外部的对象a引用了,因此产生了Closure(闭包)。
然后,这条语句的return执行的时候先执行 调用fun函数----> 把m的值传递给了fun函数中的n,把n传递给了fun函数中的o, 因此控制台中打印o的结果为0。
然而事情并没有结束,调用fun函数会返回一个新的对象,这个对象也会在内存中新开辟一个空间,而此时这个新对象中的方法fun被定义时所处环境中的变量n已经被赋值为m的值,也就是1了。
4. 当a.fun(2)执行的时候,发生了和上面一样的故事,并且内存中又被返回了一个新的对象且这个新对象中的方法fun被定义时所处环境中的变量n已经被赋值为m的值,也就是2
5. a.fun(3)执行同上,且这个新对象中的方法fun被定义时所处环境中的变量n已经被赋值为m的值,也就是3
故: a.fun(2) 和 a.fun(3) 在控制台中打印o的结果都为0,且不管你传的参数是多少,只要你没有改变a对象的值,那么输出的结果都是0,因为你传的参数都存在新的对象中了。
当时我这里存在一个疑问,每次执行n的值不是都被修改了吗,为什么结果都是0呢?
注意:因为你始终都是在调用a的方法,而你每次执行a的方法fun的时候又没有把新返回的对象重新赋值给a,所以a里面的fun方法被定义时所处环境中的变量n一直都是0
6. 当 var b = fun(0).fun(1).fun(2).fun(3); 执行的时候就和上面疑问中的情形是一样的了。
当 fun(0)执行的时候,同上面a.fun(0)一样,返回结果是undefined,且此时产生的新对象中,方法fun被定义时所处环境中的变量n为0
当 fun(1)执行的时候,相当于上面的a.fun(1)一样,都是输出0 (此时fun方法所处环境中的n为0),且返回一个新对象,新对象中的变量n为1
当fun(2)执行的时候,就不太一样了,因为是在前一条语句执行结果后面直接调用fun方法, 但此时的fun方法已经不再是fun(1)中的方法了,而是上面返回的新对象中的方法,也就是变量n为1的方法,所以,这里输出的结果为1,且返回一个新对象,新对象中的变量n为2
当fun(3)执行的时候,和fun(2)的情况一样,输出结果为新的对象中的n,也就是2
7. 到这里,var c = fun(0).fun(1); 应该就能够明白为什么这里输出对的结果是 undefined 和 0 了
因为道理和前面一样,第一个 fun(0) 给n传递了值,但是o没有,所以打印o的结果为undefined,第二个 fun(1) 将n的值传给了o, 所以打印的结果为0,且这里产生的对象被赋值给了变量c,此时方法fun被定义时的环境(也就是它的词法作用域)中的n是被重新赋值的1 (方法fun的形参m把被传过来的实参1,传递给了函数fun中的形参n)
故:c.fun(2) 和 c.fun(3)中c对象的fun方法被定义时的环境中的n都是1,所以输出的结果也是把n的值传递给fun函数中的o, 即输出1
此处是测试结果图: