谈谈JavaScript中的函数与闭包


Posted in Javascript onApril 14, 2013

闭包这东西,说难也难,说不难也不难,下面我就以自己的理解来说一下闭包

一、闭包的解释说明

对于函数式语言来说,函数可以保存内部的数据状态。对于像C#这种编译型命令式语言来说,由于代码总是在代码段中执行,而代码段是只读的,因此函数中的数据只能是静态数据。函数内部的局部变量存放在栈上,在函数执行结束以后,所占用的栈被释放,因此局部变量是不能保存的。

Javascript采用词法作用域,函数的执行依赖于变量作用域,这个作用域是在定义函数时确定的。因此Javascript中函数对象不仅保存代码逻辑,还必须引用当前的作用域链。Javascript中函数内部的局部变量可以被修改,而且当再次进入到函数内部的时候,上次被修改的状态仍然持续。这是因为因为局部变量并不保存在栈上,而是通过一个对象来保存。

决定使用哪个变量是由作用域链决定的,每次生成函数实例时,都会为之创建一个对象用来保存局部变量,并且把这个用于保存局部变量的对象加入作用域链中。不同函数对象可以通过作用域链关联起来。Javascript中所有函数都是闭包,我们不能避免“产生”闭包。

引用一张《Javascript高级程序设计》中的图来说明,虽然这张图并不完全说明所有情况。图中的activation object就是用于保存变量的对象。

谈谈JavaScript中的函数与闭包

 

简而言之,在Javascript中:

闭包:函数实例保存着在执行时所需要的变量的引用,而不会复制保存当时变量的值。(在Object C的实现中,我们可以选择保存当时的值或者是引用)

作用域链:解析变量时查找变量所在的方式,以var作为终止符号,如果链上一直没有var,则一直追溯到全局对象为止。

C#中的闭包特性是由编译器把局部变量转换成引用类型的对象成员实现的。

二、闭包的使用
 
下面通过一些具体例子来说明如何利用闭包这一特性:

1.闭包是在定义的时候产生的

function Foo(){ function A(){} function B(){} function C(){}}
我们每次执行Foo()的时候,都有有A,B,C这三个函数实例(闭包)产生,当Foo执行完毕,生成的实例没有其他引用,因此会被当成垃圾随之销毁(不一定是马上销毁)。
我们来证实一下作用域链是在函数定义时确定的,所以这里显示的应该是'local scope'

var scope = "global scope"; function checkscope() { var scope = "local scope"; function f() { return scope; } return f;}checkscope()()

同样道理:

(function(){ function A(){} function B(){} function C(){}}())
上面的表达式执行完后也会有A,B,C这三个函数实例(闭包)产生,因为这是一个立即执行的匿名函数,这三个闭包只能产生一次。生成的闭包没有其他引用,因此会被当成垃圾随之销毁(不一定是马上销毁)。

我们之所以这么写,目地有两个

1.避免污染全局对象

2.避免多次产生相同的函数实例

 

对比下面两个例子,闭包是如何保存作用域链的:

 function A(){} //比较省内存的写法,创建对象速度快,开销小 (function(prototype){ var name = "a"; function sayName () { alert(name); } function ChangeName() { name += "_changed" } prototype.sayName = sayName;//引用通过执行匿名函数产生的闭包,闭包只会产生一次 prototype.changeName = ChangeName; }(A.prototype)) var a1 = new A(); var a2 = new A();
 a1.sayName(); a1.changeName(); a2.sayName();

--------------------------------------------------------------------------------

 function B(){ //原型链比较短的做法,找到方法的速度快,但是比较耗内存,每次new 调用构造器都有2个函数实例和1个变量产生。 var name = "b"; function sayName () { alert(name); } function changeName() { name += "_changed"; } this.sayName = sayName;//引用闭包,每次调用函数B都会产生新的闭包 this.changeName = changeName; }//如果函数调用之前带有new关键字,则函数作为构造器使用。//本质上来说作为构造器和作为普通函数调用没区别。如果直接调用B(),那么this对象会绑定到全局对象,新生成的闭包会代替旧的闭包赋给全局对象的changeName和sayName属性上,因此旧的闭包会被当成垃圾回收。//如果作为构造器使用,new 关键字会生成一个新的对象(this指向这个新对象)并初始化这个新对象的sayName和changeName属性,因此每次生成的闭包都会因为有引用而保留下来。 var b1 = new B(); b1.sayName(); b1.changeName(); b1.sayName(); var b2 = new B(); b2.sayName(); b1.sayName();

三、泄漏问题:在编译语言中,函数体总在文件的代码段中,并在运行期被装入标志为可执行的内存区。事实上我们不认为函数自身会有生命周期。我们在大多数情况下会认为“引用类型的数据结构”具有生存周期和泄漏的问题,如指针、对象等。

JavaScript中内存的泄漏本质上就是定义函数时生成的保存局部变量的对象因为存在引用而不被当成垃圾被回收。

1.存在循环引用

2.有些对象总不能销毁,如IE6在DOM中的内存泄漏,或者在销毁时不能通知到Javascript引擎,因此也就有些Javascript闭包总不能被销毁。这些情况通常是发生在Javascript宿主对象和Javascript中原生对象沟通不畅导致。

Javascript 相关文章推荐
js类 from qq
Nov 13 Javascript
JavaScript实现页面滚动图片加载(仿lazyload效果)
Jul 22 Javascript
javascript移动设备Web开发中对touch事件的封装实例
Jun 05 Javascript
JQuery中DOM加载与事件执行实例分析
Jun 13 Javascript
总结JavaScript三种数据存储方式之间的区别
May 03 Javascript
Knockout结合Bootstrap创建动态UI实现产品列表管理
Sep 14 Javascript
JS+HTML5实现图片在线预览功能
Jul 22 Javascript
webpack进阶——缓存与独立打包的用法
Aug 02 Javascript
JS实现图片旋转动画效果封装与使用示例
Jul 09 Javascript
vue实现将一个数组内的相同数据进行合并
Nov 07 Javascript
jQuery实现数字华容道小游戏(实例代码)
Jan 16 jQuery
JS实现可控制的进度条
Mar 25 Javascript
在新窗口打开超链接的方法小结
Apr 14 #Javascript
JS中setTimeout()的用法详解
Apr 14 #Javascript
js验证是否为数字的总结
Apr 14 #Javascript
JS 实现Json查询的方法实例
Apr 12 #Javascript
js弹出层(jQuery插件形式附带reLoad功能)
Apr 12 #Javascript
location对象的属性和方法应用(解析URL)
Apr 12 #Javascript
图片轮换效果实现代码(点击按钮停止执行)
Apr 12 #Javascript
You might like
PHP Ajax实现页面无刷新发表评论
2007/01/02 PHP
匹配csdn用户数据库与官方用户的重合度并将重叠部分的用户筛选出来
2011/12/25 PHP
php+ajax实现文章自动保存的方法
2014/12/30 PHP
PHP单例模式模拟Java Bean实现方法示例
2018/12/07 PHP
wap手机图片滑动切换特效无css3元素js脚本编写
2014/07/28 Javascript
jQuery经过一段时间自动隐藏指定元素的方法
2015/03/17 Javascript
javascript实现可拖动变色并关闭层窗口实例
2015/05/15 Javascript
基于js实现投票的实例代码
2015/08/04 Javascript
angularJS与bootstrap结合实现动态加载弹出提示内容
2015/10/16 Javascript
基于JS实现PHP的sprintf函数实例
2015/11/14 Javascript
JavaScript数据类型学习笔记
2016/01/25 Javascript
JavaScript轮播图简单制作方法
2017/02/20 Javascript
JavaScript解析任意形式的json树型结构展示
2017/07/23 Javascript
Node.js中Bootstrap-table的两种分页的实现方法
2017/09/18 Javascript
js截取字符串功能的实现方法
2017/09/27 Javascript
mint-ui的search组件在键盘显示搜索按钮的实现方法
2017/10/27 Javascript
动手写一个angular版本的Message组件的方法
2017/12/16 Javascript
vue.js使用v-model指令实现的数据双向绑定功能示例
2018/05/22 Javascript
微信小程序仿美团城市选择
2018/06/06 Javascript
JavaScript字符串转数字的5种方法及遇到的坑
2018/07/16 Javascript
在 Angular-cli 中使用 simple-mock 实现前端开发 API Mock 接口数据模拟功能的方法
2018/11/28 Javascript
Python中的对象,方法,类,实例,函数用法分析
2015/01/15 Python
Python数据类型详解(三)元祖:tuple
2016/05/08 Python
浅谈python中的变量默认是什么类型
2016/09/11 Python
深入理解python中函数传递参数是值传递还是引用传递
2017/11/07 Python
Python读写压缩文件的方法
2020/07/30 Python
怎么解决pycharm license Acti的方法
2020/10/28 Python
Django缓存Cache使用详解
2020/11/30 Python
英国天然抗衰老护肤品品牌:Nakin Skin Care
2019/04/16 全球购物
加拿大在线隐形眼镜和眼镜店:VisionPros
2019/10/06 全球购物
争做文明公民倡议书
2014/08/29 职场文书
工作证明范本(2篇)
2014/09/14 职场文书
医院领导班子整改方案
2014/10/01 职场文书
超市员工辞职信范文
2015/05/12 职场文书
党支部审查意见
2015/06/02 职场文书
2016高考感言
2015/08/01 职场文书