一道闭包经典题目
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的输出分别是什么?
我理解的闭包:
闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
上面的题目就相当于内部return的fun读取全局声明的fun作用域内的变量。
我们需要了解的知识点:
1、JS中有几种函数类型
具名函数(命名函数)
匿名函数
2、创建函数的几种方式
① 声明函数
最普通最标准的声明函数方法,包括函数名及函数体
function fn1(){}
② 创建匿名函数表达式
创建一个变量,变量的内容为一个函数。此种方式创建的函数为匿名函数。
var fn1=function (){}
③ 创建具名函数表达式
创建一个变量,变量的内容为一个具名函数。此种方式创建的函数,只能在创建的函数内部使用test调用,在函数外部只能使用fn1来调用此函数。
var fn1=function test(){};
④ Function构造函数
可以给Function构造函数传入一个函数字符串,返回包含这个函数字符串的匿名函数。
⑤ 自执行函数
3、三个fun函数的关系如何?
分析这道题,题目中出现了三个fun,首先搞清楚三个fun之间的关系。
第一个fun函数,是标准具名函数声明,是新创建的函数,它的返回值是一个对象字面表达式,属于一个新的object。
function fun(n,o) { console.log(o) return { fun:function(m){ //... } }; }
所有匿名生成的函数都是一个新函数。所以第一个fun和第二个fun不相同,均为新创建的函数。
4、函数作用域链
声明函数时各函数都有各自的作用域,相互关联的函数形成一条作用域链,函数在执行时,从上到下(从里到外)查找使用到的变量。
在说第三个fun之前需要说下函数作用域链,在函数表达式内部能不能访问存放当前函数的变量。
① 访问对象对象内部的函数表达式
② 访问非对象内部的函数表达式
采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。
所以最内层return出去的函数,是第一个在全局var声明的fun。
5、题目分析
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的输出分别是什么?
第一行a是在调用第一个fun, 后三个a.fun均是在调用第二个fun。
第一次调用fun时,传入fun(0),n = 0, o = undefined。
第二次调用a.fun(1)时,调用的是return出的函数, m = 1 ,此时闭包取到上层函数的n值,n = 0。所以a.fun(1) 输出 0 。
第三次调用a.fun(2)时,相当于重新调用第一个fun内部return出的fun函数,m = 2,闭包取到上层函数的n,n = 0。a.fun(2)依旧是 0。
第四次同上,a.fun(3) 输出 0。
结果为 undefined, 0, 0, 0
第二行b是一条链式调用,第一个fun(0)调用的是第一个fun函数,fun(0).fun(1)调用的是第二个fun函数(根据函数作用域链,先从自身找fun,没有的话再向上查找),fun(0).fun(1).fun(2),fun(0).fun(1).fun(2).fun(3)调用都是第二个fun函数。
fun(0)传入n = 0 , o = undefined。
fun(0).fun(1),n = 0 , m = 1 此时调用到外层时,n = 1, o = 0,输出 o 的值 为0。
fun(0).fun(1).fun(2),因为闭包在第一个函数内,所以可以接着取到上次的n值为1,再次调用第二个fun时,m = 2, n = 1。相当于第一个fun的 n = 2, o = 1,所以输出 1 。
fun(0).fun(1).fun(2).fun(3),同第三步, m = 3, n = 2,相当于 n = 3, o = 2。输出 o的值 2。
结果为undefined, 0, 1, 2
理解了第二行的调用,第三行c的调用就很容易了
fun(0).fun(1),调用第一个fun传入n = 0, o = undefiend,再调用内部return的fun传入1,0, 输出 o值 0 。
c.fun(2), 调用第二个fun函数,传入2,1,输出 1。
c.fun(3), 同上,输出 1。
结果为 undefined, 0, 1, 1