什么是闭包
要理解闭包需要先理解变量作用域
作用域
明确几点:
- JavaScript的变量作用域是基于其特有的作用域链的
- JavaScript没有块级作用域
- 函数中声明的变量在整个函数中都有定义
按照作用域区分,变量有全局变量和局部变量,由于作用域链,函数内部是可以直接读取全局变量的,而函数外部无法直接读取函数内的局部变量。
闭包定义
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
闭包的用途
- 间接访问变量
- 另一个就是让这些变量的值始终保持在内存中
来看一个栗子
1 | function fn(){ |
我们通过fn暴露出来的接口访问到了函数内部的n
,同时我们对n
的值做了修改,可以看到n依然是存在于内存中的。
具体应用方面:
- 块级作用域,防止变量名污染
- 封装私有变量
闭包的注意事项
1)由于闭包会使得函数中的变量都被保存在内存中,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
闭包题目收集
让我们理论结合实(mian)践(shi)来理解以下闭包
以下皆为chrome浏览器环境运行的结果
第一题
1 | var name = "The Window"; |
拆成两步就很容易看懂
- 第一步:object.getNameFunc()等价于function(){ return this.name; }
- 第二步:(function(){ return this.name; })(),此时调用的是一个匿名行数,this指向的是window。
第二题
1 | var name = "The Window"; |
我们同样分两步
- 第一步:object.getNameFunc()===function(){ return that.name }。
- 第二步:(function(){ return that.name })(),此时的that在匿名函数里面并没有申明,所以它会向
上级
作用域寻找that
,也就是getNameFunc里面的that
,that=this
,此时的this指向调用getNameFunc
的对象object
。
第三题
1 | function foo(x) { |
这里只是函数调用哦,不是闭包哦
1 | function foo(x) { |
当你return的是内部function时,就是一个闭包。
一个函数访问了它的外部变量,那么它就是一个闭包
第四题
1 | function fun(n,o) { |
这题有点绕,我们来分解一下:
a = fun(0)
等价于执行后,console.log(o)
没有在当前以及父级作用域中寻找到o,所以输出的是undefined
,同时a被赋值为{fun:function(m){return fun(m,0)}}
。a.fun(1)
执行,简化后为(function(1){return fun(1,0)})()
===fun(1,0)
,当前作用域里面没有fun,最后找到了顶级的function fun(n,o),执行console.log(0)
1 | function fun(n,o) { |
这链式看的我好慌,我们继续分解一下
fun(0)
会输出o
,此时没有o所以为undefind
,同时fun(0)表达式执行结果为{fun:function(m){return fun(m,0)}}
(先忽略掉后面的fun(1)、fun(2)、fun(3))。- 再执行
fun(1)
等价于执行(function(1){return fun(1,0)})()
,继续输出o
,此时o为0
,表达式简化为{fun:function(m){ return fun(m,1)}}
- 同理继续执行,依次输出1,2