谈谈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 相关文章推荐
jqgrid 表格数据导出实例
Nov 21 Javascript
javascript实现window.print()去除页眉页脚
Dec 30 Javascript
javascript适合移动端的日期时间拾取器
Nov 10 Javascript
JavaScript ParseFloat()方法
Dec 18 Javascript
基于Bootstrap的Metronic框架实现条码和二维码的生成及打印处理操作
Aug 29 Javascript
webpack4.x开发环境配置详解
Aug 04 Javascript
Vue-router的使用和出现空白页,路由对象属性详解
Sep 03 Javascript
详解处理Vue单页面应用SEO的另一种思路
Nov 09 Javascript
JavaScript判断对象和数组的两种方法
May 31 Javascript
小程序如何自主实现拦截器的示例代码
Nov 04 Javascript
JS实现可控制的进度条
Mar 25 Javascript
绘制微信小程序验证码功能的实例代码
Jan 05 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
ThinkPHP+EasyUI之ComboTree中的会计科目树形菜单实现方法
2017/06/09 PHP
Javascript----文件操作
2007/01/18 Javascript
神奇的代码 通杀各种网站-可随意修改复制页面内容
2008/07/17 Javascript
JavaScript 对Cookie 操作的封装小结
2009/12/31 Javascript
js获取 type=radio 值的方法
2014/05/09 Javascript
javascript初学者常用技巧
2014/09/02 Javascript
js查找节点的方法小结
2015/01/13 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
html+js实现简单的计算器代码(加减乘除)
2016/07/12 Javascript
JS中parseInt()和map()用法分析
2016/12/16 Javascript
基于JS设计12306登录页面
2016/12/28 Javascript
JavaScript实现图像模糊化的方法实例
2017/01/15 Javascript
Agularjs妙用双向数据绑定实现手风琴效果
2017/05/26 Javascript
nginx配置React静态页面的方法教程
2017/11/03 Javascript
JavaScript框架Angular和React深度对比
2017/11/20 Javascript
VUE接入腾讯验证码功能(滑块验证)备忘
2019/05/07 Javascript
用JavaScript实现贪吃蛇游戏
2020/10/23 Javascript
在vue中给后台接口传的值为数组的格式代码
2020/11/12 Javascript
[50:34]VGJ.T vs Fnatic 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python处理csv数据的方法
2015/03/11 Python
Python SVM(支持向量机)实现方法完整示例
2018/06/19 Python
在Django的View中使用asyncio的方法
2019/07/12 Python
基于python+selenium的二次封装的实现
2020/01/06 Python
KIKO MILANO英国官网:意大利知名化妆品和护肤品品牌
2017/09/25 全球购物
买卖正宗运动鞋:GOAT
2019/12/06 全球购物
如何减少垃圾回收让内存更加有效使用
2013/10/18 面试题
线程的基本概念、线程的基本状态以及状态之间的关系
2012/10/26 面试题
Unix里面如何在后台运行程序
2016/10/14 面试题
药剂专业学生求职信范文
2013/12/28 职场文书
婚庆公司的创业计划书
2014/01/22 职场文书
《商鞅南门立木》教学反思
2014/02/16 职场文书
自我推荐信范文
2014/05/09 职场文书
优秀乡村医生事迹材料
2014/05/28 职场文书
基层干部个人对照检查及整改措施
2014/10/28 职场文书
教师个人成长总结
2015/02/11 职场文书
Python 多线程处理任务实例
2021/11/07 Python