JS-闭包

92 阅读3分钟
function test1(){
    function test2(){
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2();
}
var c = 3;
var test3 = test1();
test3();

当test1函数被定义时

function test1(){}
var c = 3;
var test3 = test1();

系统生成[[scope]]属性,[[scope]]保存该函数的作用域链

函数test1
[[scope]]scope chain

该作用域链的第0位存储当前环境下的全局执行期上下文GO

scope chain
0GO

GO存储全局下的所有对象

GO
thiswindow
window(object)
document(object)
test1(function)
c3
test3(function)

test1函数被执行时(前一刻),test2函数被定义

function test1(){
    function test2(){}
    var a = 1;
    return test2();
}
var c = 3;
var test3 = test1();

这两的作用域链一样

scope chain
0test1 的AO
1GO
test1 的AO
thiswindow
arguments[]
test2(function)
a1
GO
thiswindow
window(object)
document(object)
test1(function)
c3
test3(function)

test1函数被执行结束时

return test2() -> 这时的test2()没有执行

test1函数被执行结束时,因为test2被返回到外部,且被全局变量test3接受,test2抓着test1的AO。

所以test1的AO并没有被销毁,因为还存在引用,只是test1作用域链对test1的AO这条指向被剪断了。

test1 scope chain
0GO

test2的作用域链还连着

test2 scope chain
0test1 的AO
1GO

也就是说test1结束了,test2脱离test1到全局里了,同时带走了test1的作用域

test3执行时

function test1(){
    function test2(){
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2();
}
var c = 3;
var test3 = test1();
test3();

因为在test1函数里test2被return出去了,test2变成全局的了

test2变成全局之后,test1就执行完毕了

test3接受的是test2(test1的返回值)

所以当test3执行时

test2的作用域链会增加自己的AO

scope chain
0test2 的AO
1test1 的AO
2GO
test2 的AO
thiswindow
arguments[]
b2
test1 的AO
thiswindow
arguments[]
test2(function)
a1
GO
thiswindow
window(object)
document(object)
test1(function)
c3
test3(function) -> function test2(){}

当打印a的时候,在自己的AO没找到,则向test1的AO查找

执行test3,实际上操作的是test2,并且,不仅可以操作test2的AO,也可以操作test1的AO

function test1(){
    function test2(){
        var b = 2;
        a = 2; //所以当更改a的值的时候是可以更改的,并且test2的AO是没有a的,a的值是改的test1的AO的值
        console.log(a); //2
    }
    var a = 1;
    return test2();
}
var c = 3;
var test3 = test1();
test3();

test3执行结束后

test2的AO被销毁,但原来test1的AO仍然存在并且被test2连着

test2 scope chain
0test1 的AO
1GO

总结:

当内部函数被返回到外部并保存时,一定会产生闭包,闭包是保存原有的作用域链不释放。过度的闭包可能会导致内存泄漏(内存占用过多),或加载过慢

例:累加减

function test(){
    var n = 100;
    function add(){
        n++;
        console.log(n);
    }
    function reduce(){
        n--;
        console.log(n);
    }
    return [add, reduce];
}
var arr = test();
arr[0]();  //101
arr[1]();  //100

因为add, reduce共用一个test的AO,可以直接改n的值

这就是数据缓存,只需要执行返回到外部的函数,就可以操作数据了

例:

function sunSched(){
	var sunSched = '';
	var operation = {
		setsShed: function(thing){
			sunSched = thing;
		},
		showSched: function(){
			console.log('My schedule on sunday is ' + sunSched);
		}
	}
	return operation;
}
var sunSched = sunSched();

sunSched.setsShed('studging');
sunSched.showSched();