一、什么是闭包?
闭包是能访问其他函数作用域的函数。
分析一下这句话就是:
1、闭包是一个函数;
2、闭包不仅能访问自己的作用域,还可以访问其他函数的作用域。
二、怎么创建闭包?
只要在一个函数中再定义一个函数,这个内部函数就是闭包。
注意:只要满足一个函数在另一个函数的内部的条件,这个内部函数就是闭包,不管这个内部函数是以怎样的形式存在于外层函数中的。下面举几个例子:
形式一:
function father () {
function son () {
}
}
形式二:
function father () {
var object = new Object();
object.son = function () {
}
}
形式三:
function father () {
var object = {
son : function(){
}
};
}
以上三种形式定义的内部函数都是闭包。
- 形式一:在一个函数中又定义了个函数,这完全符合闭包的定义,不用多说。
- 形式二:在一个函数中先创建了一个对象,然后在对象内部定义了函数,这也是闭包。
- 形式三:本质上和形式二一样,也是先在函数内部定义了一个对象,再在对象内部定义了个闭包。只不过定义对象的方式和形式二有所区别。
三、闭包的原理
上一篇讲了作用域链和执行环境,一般来讲,当函数执行完毕后,其执行环境就会从执行环境栈弹出,变量对象也会被销毁,内存中仅保存全局作用域。但是,闭包不是这样子的:
var name = "The window";//全局变量
function myObject () {
var name = "my Object";
return function () {
return name;
}
}
var result = myObject();//result就是那个匿名函数
result();//"my Object"//访问到了myObject()的局部变量name

当匿名函数从myObject()中被返回时,它的作用域链初始化为包含其自身变量对象、myObject()变量对象和全局变量对象。这样匿名函数就可以访问myObject()和全局环境中的定义的变量。最关键的是,myObject()函数执行完毕后,其变量对象也不会被销毁,因为匿名函数的作用域链仍然引用着这个活动对象。也就是说,虽然myObject()函数return之后,它执行环境弹出了执行环境栈,作用域链被销毁,但是它的变量对象仍然在内存中,直到匿名函数被销毁。
综上所述,闭包之所以能访问其外层函数作用域中的变量,是因为闭包的作用域链中存在外层函数的变量对象。即使外层函数执行结束,但由于其变量对象仍然被内层函数的作用域引用,因此不会被内存回收,直到闭包执行结束后,外层函数的变量对象才会被回收。
四、闭包的常考题
若闭包在外层函数执行结束后执行,那么它只能获取到外层函数中所有变量的最终状态。
- 例1:
function father(){
var array = [];
for (var i=0; i<10; i++) {
array[i] = function(){
return i;
}
}
return array;
}
father函数执行后会返回一个包含闭包的数组,每个闭包都会返回i。
由于这里的闭包调用时,外层函数早就执行结束了,外层函数变量对象中i值已经变成了9,此时不管执行array中的哪个闭包,返回的结果都是9。
但是,如果在闭包外层函数执行过程中立即执行闭包,那么结果就不一样了,请看例2.
- 例2:
function father(){
var array = [];
for (var i=0; i<10; i++) {
array[i] = (function(){
return i;
})();
}
return array;
}
此时的闭包在外层函数执行时就立即执行,在那个时刻,闭包中i的值就是外层函数当前i的值,因此返回的array中存储的将是0-9。
参考:https://blog.youkuaiyun.com/u010425776/article/details/53709111
https://blog.youkuaiyun.com/xiaozhuxmen/article/details/52281301
再次感谢各位大牛的技术分享,受益匪浅!如有理解不对的地方,还望各位博友多多指点~