做用域和做用域链:javascript
参考文章 :http://www.cnblogs.com/malinlin/p/6028842.html html
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html前端
http://www.zhangyunling.com/?p=134java
https://segmentfault.com/a/1190000000652891segmentfault
总结: 闭包
① js中到处是对象函数
②函数执行时会建立一个执行环境和变量对象性能
③代码在执行环境中运行 变量对象会按照顺序存到做用域链中this
④执行一次函数就会建立一个新的活动对象,就会有新的做用域链,多个做用域链互不干扰.spa
⑤引用函数不消失,活动变量就一直存在 , 闭包使用完了以后 将引用变量指向null,释放内存.
1. 全局做用域(Global Scope)
(1)最外层函数和在最外层函数外面定义的变量拥有全局做用域
(2)全部没有定义直接赋值的变量,自动声明为拥有全局做用域
(3)全部window对象的属性拥有全局做用域,例如window.name、window.location 以下:定时器里指向的是全局的函数
setTimeout("C()",1000)==setTimeout("this.C()",1000)
1 var a = 1; 2 function B(){ 3 var a = 2; 4 setTimeout("C()",1000); 5 setTimeout(C,2000); 6 function C(){ 7 alert("a="+a); 8 } 9 } 10 function C(){ 11 alert("a="+a); 12 } 13 B();
2. 局部做用域(Local Scope)
局部做用域通常只在固定的代码片断内可访问到,最多见的例如函数内部
1. 执行环境和活动对象
函数在执行时 生成执行环境 和 变量对象 ,当代码在一个执行环境中执行时,会建立变量对象的一个做用域链(scope chain)
执行环境(execution context)定义了变量或者函数有权访问的其余数据,每一个执行环境都有一个与之关联的变量对象(variable object),执行环境中定义的变量和函数就保存在这个变量对象中;
全局执行环境是最外围的一个执行环境,一般被认为是window对象
执行环境中的全部代码执行完之后,执行环境被销毁,保存在其中的变量和函数也随之销毁;(全局执行环境到应用退出时销毁).
在闭包,每次执行A函数时,都会生成一个A的活动变量和执行环境,执行完毕之后,A的执行环境销毁,可是活动对象因为被闭包函数引用,因此仍然保留,因此闭包使用完了以后 将引用变量指向null
3. 做用域链(Scope Chain)
当代码在一个执行环境中执行时,会建立变量对象的一个做用域链(scope chain),做用域链用来指定执行环境有权访问的全部变量和函数的访问顺序;当一个函数建立后,它的做用域链会被建立此函数的做用域中可访问的数据对象填充。
做用域链的最前端,始终是当前代码执行环境的变量对象,若是这个环境是函数,则其活动对象就是变量对象
做用域链的下一个变量对象,来自外部包含环境,再下一个变量对象,来自下一个外部包含环境,以此类推直到全局执行环境
在函数执行过程,根据当前执行环境的做用域链来逐层向外查找变量,而且进行标识符解析
这些值按照它们出如今函数中的顺序被复制到运行期上下文的做用域链中。 它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象 包含了函数的全部局部变量、命名参数、参数集合以及this,而后此对象会 被推入做用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。
在函数执行过程当中,没遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从做用域链头部,也就是从活动对象开始搜索,查找同名的标识符,若是找到了就使用这个标识符对应的变量,若是没找到继续搜索做用域链中的下一个对象,若是搜索完全部对象都未找到,则认为该标识符未定义。函数执行过程当中,每一个标识符都要经历这样的搜索过程。从做用域链的结构能够看出,在运行期上下文的做用域链中,标识符所在的位置越深,读写速度就会越慢。在编写代码的时候应尽可能少使用全局变量,尽量使用局部变量。一个好的经验法则是:若是一个跨做用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。
闭包:
1.闭包能够访问函数中的变量。
2.可使变量长期保存在内存中,生命周期比较长。但闭包不能滥用,不然会致使内存泄露,影响网页的性能。闭包使用完了后,要当即使用资源,将引用变量指向null。
<script> function A(){ var x = 1; return function(){ x++; console.log(x); } } var m1 = A();//第一次执行A函数 m1();//2 m1();//3 var m2 = A();//第二次执行A函数 m2();//2 m1();//4 </script>
1.(为何连续执行m1的时候,x的值在递增?)
answer:由于m1在引用的活动对象A一直没有释放(想释放的话可让m1=null),因此x的值一直递增。
2.定义函数m2的时候,为何x的值从新从1开始了?
answer:由于又一次运行了A函数,生成一个新的A的活动对象,因此m2的做用域链引用的是一个新的x值。
3.m1和m2里面的x为何是相互独立,各自维持的?
answer:由于在定义m1和m2的时候,分别运行了A函数,生成了两个活动对象,因此,m1和m2的做用域链是指向不一样的A的活动对象的。
好的,到这里先回顾一下前面说到的知识点:
执行环境和变量对象在运行函数时生成
执行环境中的全部代码执行完之后,执行环境被销毁,保存在其中的变量和函数也随之销毁;(全局执行环境到应用退出时销毁)
闭包常见题目:
1 function f1(){ 2 var n=999; 3 nAdd=function(){n+=1} 4 function f2(){ 5 alert(n); 6 } 7 return f2; 8 } 9 var result=f1(); 10 result(); // 999 11 nAdd(); //首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包, 12 result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。
为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另外一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。
闭包中this的指向:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; document.write(object.getNameFunc()());//The Window 匿名函数的执行环境具备全局性,所以其this对象一般指向window