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 | |
|---|---|
| 0 | GO |
GO存储全局下的所有对象
| GO | |
|---|---|
| this | window |
| window | (object) |
| document | (object) |
| test1 | (function) |
| c | 3 |
| test3 | (function) |
test1函数被执行时(前一刻),test2函数被定义
function test1(){
function test2(){}
var a = 1;
return test2();
}
var c = 3;
var test3 = test1();
这两的作用域链一样
| scope chain | |
|---|---|
| 0 | test1 的AO |
| 1 | GO |
| test1 的AO | |
|---|---|
| this | window |
| arguments | [] |
| test2 | (function) |
| a | 1 |
| GO | |
|---|---|
| this | window |
| window | (object) |
| document | (object) |
| test1 | (function) |
| c | 3 |
| test3 | (function) |
test1函数被执行结束时
return test2() -> 这时的test2()没有执行
test1函数被执行结束时,因为test2被返回到外部,且被全局变量test3接受,test2抓着test1的AO。
所以test1的AO并没有被销毁,因为还存在引用,只是test1作用域链对test1的AO这条指向被剪断了。
| test1 scope chain | |
|---|---|
| 0 | GO |
test2的作用域链还连着
| test2 scope chain | |
|---|---|
| 0 | test1 的AO |
| 1 | GO |
也就是说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 | |
|---|---|
| 0 | test2 的AO |
| 1 | test1 的AO |
| 2 | GO |
| test2 的AO | |
|---|---|
| this | window |
| arguments | [] |
| b | 2 |
| test1 的AO | |
|---|---|
| this | window |
| arguments | [] |
| test2 | (function) |
| a | 1 |
| GO | |
|---|---|
| this | window |
| window | (object) |
| document | (object) |
| test1 | (function) |
| c | 3 |
| 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 | |
|---|---|
| 0 | test1 的AO |
| 1 | GO |
总结:
当内部函数被返回到外部并保存时,一定会产生闭包,闭包是保存原有的作用域链不释放。过度的闭包可能会导致内存泄漏(内存占用过多),或加载过慢
例:累加减
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();