这是概念:
运行期上下文:
函数执行的时候,会创建一个称为执行期上下文的内部对象。(类似于我们在预编译中所讲的AO对象)一个执行期上下文定义一个函数执行时的环境。函数每次执行时对应的执行期上下文 都是独一无二的,所以多次调用同一个函数会导致产生多个执行期上下问,当函数执行完毕,它所产生的执行期上下文将被销毁。
作用域:
[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链:
[[scope]]中存储的集合呈链式链接,这种链式链接被称为作用域链。
其实 [[scope]]中存储的是作用域链,作用于域由多个执行期上下文链接起来
在那个查找变量:从这个函数作用域链的顶端依次向下查找。
认识作用域:
那我们用一个例子来说起作用域吧
function a(){
function b(){
var b=234;
}
var a=123;
b();
}
var glob=100;
a();
作用域的分析:(注:所有的AO 都是同一个AO)
//a 的作用域
a defined a.[[scope]] -->0:GO{} //a 被定义的时候
a doing a.[[scope]] -->0:AO{} //a 被执行的时候
-->1:GO{}
//a 执行产生了 b 的定义(b在a中产生,保存a的成果)
b defined b.[[scope]] -->0:a AO{} //b 被定义的时候
-->1:GO{}
b doing b.[[scope]] -->0:b AO{} //b 被执行的时候
-->1:a AO{}
-->2:GO{}
来看一下图解:
当b 函数执行完毕后 他会销毁自己之前的执行期上下文,即b 自己的AO 指向线被切断了。
再次b的执行完毕伴随a 函数的执行完毕
与b相同 a 自己的AO 指向线被切断了。至此所有的执行完毕
再用一个例子我们来分析一下吧:
function a(){
function b(){
function c(){
}
c(); //c 的调用语句
}
b(); //b 执行才能导致 c定义
}
a(); //a 执行才能导致 b定义
// a defined a.[[scope]] -->0:GO
// a doing a.[[scope]] -->0:a AO
-->1:GO
// b defined b.[[scope]] -->0:a AO
-->1:GO
// b doing b.[[scope]] -->0:b AO
-->1:b AO
-->2:GO
// c defined c.[[scope]] -->0:b AO
-->1:a AO
-->2:GO
// c doing c.[[scope]] -->0:c AO
-->2:b AO
-->3:a AO
-->4:GO
- 所有的 a AO 都是同一个。(b,c 也一样)
一个闭包过程!
我们来用一个例子引出闭包吧:
function a(){
function b(){
var bbb=234;
console.log(aaa); //结果:123
}
var aaa=123;
return b;
}
var glob =100;
var demo =a();
demo();
我们来分析一下:
1) var demo =a(); a 执行,产生自己的作用域链AO GO。
2)a的执行伴随 b的定义(b 函数仅仅定义,并没有被执行),b在a中定义 所以它的作用域链与a 相同。
3)return b;将 b的结果保存在demo 中,至此a 执行完毕 ,a 与AO的连线被剪断。
4)demo(); 执行b 此时b仍保存着 a 的AO。(a 只是单方面切断了自己的链接)
现在问题来了,a执行完毕剪断了自己与自己AO的连线,就算b 执行完自己切断了自己的AO(b 的AO)连线,但是return b;demo 将b函数保存了下来,这个函数任然存在,a 的AO 的连线就一直没有断。除非将 (demo=null;) 这个a AO对象才会被销毁。——这就是闭包
总结一下:当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域不释放,造成内存泄漏。
闭包有什么作用呢?
1)实现公有变量
2)可以做缓存(存储结构)
3)可以实现封装,属性私有
4)模块化开发,防止污染全局变量
神奇的闭包:
function text(){
var arr=[];
for(var i=0;i<10;i++)
{
arr[i]=function(){
document.write(i+' ');
}
}
return arr;
}
var myArr=text();
for(var j=0;j<10;j++){
myArr[j]();
}
输出结果为 10个10
这个结果和你想的一样吗?
来 我们来分析一下:
function text(){ //text 函数与 其内部函数形成闭包
var arr=[];
//var i=0;
for(var i=0;i<10;i++) //i 变量也在text 中产生,相当在上面声明 i
{
arr[i]=function(){ //(函数没有执行 仅仅是引用,相当于把函数折叠起来,系统暂时没有执行函数内部的语句)
document.write(i+' ');
}
}
return arr; //返回数组,数组中存储着 未被执行的十个函数
}
var myArr=text();
for(var j=0;j<10;j++){
myArr[j](); //因为实现闭包,内部函数所共享的 i是在text中产生的,就是i存储在text AO中。
//这十个函数都连着这个同一个text AO,所有他们的 i为同一个 i。
}
现在你懂了这个结果的原因,那如何解决这个问题呢?
解决闭包:
//立即执行函数:创建一个独立的作用域。
//这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
function text(){
var arr=[];
for(var i=0;i<10;i++)
{
(function(j){ //立即执行函数读到马上执行
arr[j]=function(){ //里面的函数仍不被执行 因为不管立即函数执行不执行里面的函数都只是一条函数声明语句
document.write(j+' ');
}
}(i)); //将实参i 的值赋给j,此后j的值一直不变
}
return arr;
}
var myArr=text();
for(var j=0;j<10;j++){
myArr[j]();
}