JavaScript---闭包和作用域链
作用域和作用域链:
参考文章 :http://www.cnblogs.com/malinlin/p/6028842.html
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
http://www.zhangyunling.com/?p=134
https://segmentfault.com/a/1190000000652891
总结:
① js中处处是对象
②函数执行时会创建一个执行环境和变量对象
③代码在执行环境中运行 变量对象会按照顺序存到作用域链中
④执行一次函数就会创建一个新的活动对象,就会有新的作用域链,多个作用域链互不干扰.
⑤引用函数不消失,活动变量就一直存在 , 闭包使用完了之后 将引用变量指向null,释放内存.
1. 全局作用域(Global Scope)
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域
(2)所有没有定义直接赋值的变量,自动声明为拥有全局作用域
(3)所有window对象的属性拥有全局作用域,例如window.name、window.location 如下:定时器里指向的是全局的函数
setTimeout("C()",1000)==setTimeout("this.C()",1000)
var a = 1;
function B(){
var a = 2;
setTimeout("C()",1000);
setTimeout(C,2000);
function C(){
alert("a="+a);
}
}
function C(){
alert("a="+a);
}
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();//
m1();//
var m2 = A();//第二次执行A函数
m2();//
m1();//
</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的活动对象的。
好的,到这里先回顾一下前面说到的知识点:
执行环境和变量对象在运行函数时生成
执行环境中的所有代码执行完以后,执行环境被销毁,保存在其中的变量和函数也随之销毁;(全局执行环境到应用退出时销毁)
闭包常见题目:
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); //
nAdd(); //首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,
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
JavaScript---闭包和作用域链的更多相关文章
- javascript闭包和作用域链
最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...
- Javascript——闭包、作用域链
1.闭包:是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式:在一个函数内部创建另一个函数. function f(name){ return function(object){ var ...
- Javascript中闭包的作用域链
作用域定义了在当前上下文中能够被访问到的成员,在Javascript中分为全局作用域和函数作用域,通过函数嵌套可以实现嵌套作用域. 闭包一般发生在嵌套作用域中.闭包是JavaScript最强大的特性之 ...
- javascript笔记:javascript的关键所在---作用域链
javascript里的作用域是理解javascript语言的关键所在,正确使用作用域原理才能写出高效的javascript代码,很多javascript技巧也是围绕作用域进行的,今天我要总结一下关于 ...
- javascript的关键所在---作用域链
javascript的关键所在---作用域链 javascript里的作用域是理解javascript语言的关键所在,正确使用作用域原理才能写出高效的javascript代码,很多javascript ...
- [ JS 进阶 ] 闭包,作用域链,垃圾回收,内存泄露
原网址:https://segmentfault.com/a/1190000002778015 1. 什么是闭包? 来看一些关于闭包的定义: 闭包是指有权访问另一个函数作用域中变量的函数 --< ...
- JS闭包、作用域链、垃圾回收、内存泄露相关知识小结
补充: 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 闭包的三个特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变 ...
- JavaScript 中的闭包和作用域链(读书笔记)
要想理解闭包,应当先理解JavaScript的作用域和作用域链. JavaScript有一个特性被称之为“声明提前(hoisting)”,即JavaScript函数里声明的所有变量(但不涉及赋值)都被 ...
- javascript深入浅出图解作用域链和闭包
一.概要 对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数. 关键点: 1.闭包是一个函数 2.能够访问另外一个函数作用域中的变量 文章首发地址于sau交流学习社 ...
- [译]JavaScript:函数的作用域链
原文:http://blogs.msdn.com/b/jscript/archive/2007/07/26/scope-chain-of-jscript-functions.aspx 在JavaScr ...
随机推荐
- 为什么要用rem
为什么要用rem 参考文章web app变革之rem 公司使用的375*667(也就是iPhone6)作为缩放比例标准,设计师是按照750px的标准出图 为了保证在不同的屏幕下显示效果基本等同,为此规 ...
- 关于JS闭包,作者不详(转)
说明:本文由两篇文章结合而成,系从他人笔记中转过来的, 具体作者不详.因为觉得不错,遂共享之.如有侵权,立删致歉. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变 ...
- 解决SharePoint文档库文件在搜索结果页面显示的标题和文档的标题不一致问题(search result)
问题表现: SharePoint 2013 爬网后,搜索一个文档,虽然搜到了,但是显示有点问题,如图: 原因分析: 造成该问题的原因是,该文档除了本身有一个名称外,在文档metadata的title属 ...
- iOS 获取UIView所在的VIewController
写程序的时候我们经常要封装代码,当你封装了一个UIView的子类里需要调用所在ViewController的方法用如下代码调取所在的VIewController #pragma mark - 获取所在 ...
- Mac 以太网连接 报无效的服务器地址 BasicIPv6ValidationError
Mac 以太网连接 报无效的服务器地址 BasicIPv6ValidationError 用Mac这么久,一直是用WiFi连接网络,没搞过以太网连接,我也是醉了 显然 Mac 不能像 Windows ...
- Python数据结构与算法--数据类型
从数据类型开始 Python支持面向对象的编程范式,这意味着Python把数据看成解决问题的关键. 在Python中,类似其他的面向对象的编程语言, 我们定义一个类,用来描述数据是什么 (状态) 和数 ...
- Android中使用抖动动画吸引来用户注意
原文:http://www.androidcn.org/topic/552e65bc61d460226ab27a5c 在应用中,有时候我们要吸引用户去点击某些按钮,比如应用市场的推荐按钮,为了能够吸引 ...
- 【转】Visual Studio 非常实用的调试技巧
下面有从浅入深的6个问题,您可以尝试回答一下 一个如下的语句for (int i = 0; i < 10; i++){if (i == 5)j = 5;},什么都写在一行,你怎么在j=5前面插入 ...
- 通知中心NSNotificationCenter的使用
通知中心NSNotificationCenter的使用 Cocoa框架中,通知中心以及KVO都属于设计模式中的观察者. Source 在使用通知中心之前,对通知中心类进行了简单的封装,以便可读性更强. ...
- select_tag 选择后自动提交,并且保持选择的项
使用select来选择,,,选择就响应真是花了好大的功夫....你妹...在controller设置变量来储存选择后的值...js中如果select选择为空那么传入的值是””而不是null selec ...