闭包
但凡是内部的函数被保存到了外部,一定生成闭包,因为内部的函数一定会保存它上一级函数的劳动成果,它还没有被执行就被扔到了外部,这样的话就算它上一级函数执行完了,要销毁了自己,但是它的AO已经被内部函数保存到了外部,所以想销毁都销毁不了。
案例分析1
举个例子:
1, a函数定义生成全局的上下文,a 函数执行的生成了自己的AO对象放在作用域链顶端的同时,b函数被定义,保留了a的劳动成果,指向和a一样的AO和GO。并且被return出去(就是被保存到了外面)
2,当return语句结束的时候,就是a函数执行完毕的之后,这个时候a执行完了,它所产生的执行上下文被销毁,就是砍断自己的AO对象(函数执行完毕要销毁的都是自己的AO对象,回到被定义的时候的状态)(被定义的时候的状态如果是内部函数那么状态就是保留了上一级函数的劳动成果)。但是由于b函数被保存出去了,所以b函数还是依旧保留了a函数的执行期上下文AO。
案例分析2
function a(){
var num = 100;
function b(){
num++;
console.log(num);
}
return b;
}
var demo = a();
demo();//101
demo();//102
- 首先函数a定义的时候,有一个全局的上下文GO对象,
- 函数a执行的时候有上下文AO对象
aAO {num :100;}
放在作用域链顶端,b函数被定义的时候就会有GO和aAO {num :100;}
(也就是a的劳动成果), - b函数没有执行就被返回到了外部,这个时候a函数执行完毕了,要销毁自己的aAO对象。
- 由于b保留了a的劳动成果,所以在b函数执行的时候就会有
GO,aAO {num :100;},bBO{}
, - 这时候执行第一个
demo()
的时候num++;
操作,b在自己的bBO{}
里面没有找到num
,就去aAO{num :100;}
里面找,打印101和aAO变成{num :101;},。 - 然后b函数被执行完了,销毁自己的
bBO{}
,回到定义的状态就会有GO和aAO {num :101;}
. - 等待再次执行
demo()
的时候,b函数重新生成一个独一无二的bBO{}
(和刚才的那个不一样,刚才的那个已经被销毁了),这个时候b在自己的bBO{}
里面没有找到num
,就去aAO{num :101;}
里面找,打印101和aAO变成{num:102;}。
闭包的危害
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。(内存泄漏:内存被占用了,内存被占用的越多,剩下的内存可以利用的就越少,就叫做内存泄漏了。)
闭包的作用
1, 实现公有变量:例如函数累加器
function add(){
var count = 0;
function demo(){
count++;
console.log(count);
}
return demo;
}
var counter = add();
counter();//1
counter();//2
counter();//3
counter();//4
counter();//5
counter();//6
counter();//7
2,可以做缓存(存储结构)
a和test形成闭包,b也和test形成闭包。
function test(){
var num = 100;
function a(){
num++;
console.log(num);//101
}
//a:defined a.[[scope]] 0 : testAO
// 1 : GO
function b(){
num--;
console.log(num);//100
}
//b:defined b.[[scope]] 0 : testAO
// 1 : GO
return [a,b];
}
var myArr = test();
myArr[0]();
//a:doing a.[[acope]] 0 : aAO
// 1 : testAO
// 2 : GO
myArr[1]();
//a:doing a.[[acope]] 0 : aAO
// 1 : testAO
// 2 : GO
function eater(){
var food = "";
var obj = {
eat : function(){
console.log("i am eating" + food);
food = "";
},
push : function(myFood){
food = myFood;
}
}
return obj;
}
var eater1 = eater();
eater1.push('banna');
eater1.eat();
变量food被当做隐式的存储结构来用
3,闭包实现封装,属性私有化
function Deng(name, wife) {
var prepareWife = "xiaozhang";
this.name = name;
this.wife = wife;
this.divorce = function () {
this.wife = prepareWife;
}
this.changePrepareWife = function (target) {
prepareWife = target;
}
this.sayPrepareWife = function (target) {
console.log(prepareWife);
}
}
var deng = new Deng('deng', 'xiaoliu');
分析:
deng.prepareWife//undefined
divorce = function () {
this.wife = prepareWife;
}
changePrepareWife = function (target) {
prepareWife = target;
}
sayPrepareWife = function (target) {
console.log(prepareWife);
}
这三个函数在this里面,当new一个对象的时候,返回this, 那么这三个函数与Deng形成三个闭包,都拥有Deng的AO,可以操作prepareWife变量
但是,在外部通过对象deng.prepareWife去访问时访问不到的,就好像这个变量是这个对象私有的,只有它自己能看到, 或者它自己设置方法可以让你看到。
4,模块化开发,防止污染全局变量
闭包实现命名空间,管理变量,防止污染全局,适用于模块化开发。
var name = 'bcd';
var init = (function () {
var name = 'abc';
function callName() {
console.log(name);
}
return function () {
callName();
}
}())
var initQS = (function () {
var name = 'efd';
function callName() {
console.log(name);
}
return function () {
callName();
}
}())
init();//abc
initQS();//efd
这样的方式在一调用init()或者initQS()函数的时候,就会启动立即执行函数,然后函数执行完返回的就是自己执行的这个callName函数,
这样即使使用同样的函数名和变量名也不会相互干扰或者覆盖。