江湖上都说要了解闭包,得先了解作用域链,所以,先从作用域链开始吧。
作用域链
- 作用域链是一个对象列表或链表,这组对象定义了这段代码“作用域”中的变量;
- 每当调用一个函数,这个函数会创建一个新的对象来储存它的变量(变量绑定对象),并且将这个对象添加到作用域链上;当函数返回时,就从作用域链中将这个对象删除;
- 当javascript需要查找一个变量时,它会沿着作用域链一级一级地搜索变量。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至作用域链的最顶层(全局对象)为止。
- 对于嵌套函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链都指向一个变量绑定对象
我们来看一个栗子
|
|
上例的作用域链就是:sayHello[word="sayHello"]
——sayWord[word="sayWord"]
——window[word="the window"];
当执行sayHello函数时,会沿着这个作用域链一级一级往上找word这个变量,直到找到为止。
闭包
javacript高级程序设计上说“有不少开发人员总是搞不清匿名函数和闭包这两个概念”。很遗憾,本人就是。
闭包是指有权访问另一个函数作用域中的变量的函数。
闭包是从用户角度考虑的一种设计概念,它基于对上下文的分析,把龌龊的事情、复杂的事情和外部环境交互的事情都自己做了,留给用户一个很自然的接口。
函数对象通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内
上栗子
|
|
sayHello函数在sayWord函数内部,它能访问sayWord函数内部的变量。sayHello函数就是闭包,
利用闭包实现私有属性
|
|
每次调用函数都会创建变量绑定对象添加到作用域链中,所以每次调用外部函数的时候,作用域链都是不同的。而对于嵌套函数,每次调用外部函数时,内部函数又会重新定义一遍。
闭包存在的问题
this对象的指向问题
|
|
每个函数在被调用时都会自动取得两个特殊变量:this
和arguments
。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。
这里
object.getNameFunc()()==(function(){return this.name;})()
所以其活动对象为window;
解决办法
内存泄露问题
- 首先调用say();结果输出hello
- 然后调用add,add是个全局变量,所以可以在外部调用,因为它又是闭包,所以可以访问到变量word,所以world=“hello world”;
- 最后再调用say();发现结果输出hello world;
这说明函数sayWord中的局部变量word一直保存在内存中,并没有在sayWord调用后被自动清除。产生这个问题主要是由于匿名函数保存了一个对word的引用,所以它所占用的内存就永远不会被回收。
注意:因为闭包的这个特性,所以外部函数的变量是其内部所有闭包的共享值,因此,不能在闭包中随意的改变外部函数的变量值,牵一发而动全身。