js闭包(二)
2016-03-05 19:04
573 查看
一、何谓“闭包”?
所谓“闭包(Closure)”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
描述的如此学术的官方解释,相信很少人能够理解,其实所有的函数都是一个闭包,不过我们所说的“闭包”通常指的是函数嵌套所产生的闭包。这种闭包更 为强大,它可以用优雅的方式来处理一些棘手的问题,有些程序员戏谑没有闭包简直就活不下去了,这虽然有些夸张,但从侧面却恰恰说明闭包有着非常强大的功 能。
这就是一个使用闭包的简单示例,代码执行完毕后,函数对象并不会被垃圾回收机制(Garbage Collection)回收,函数内的临时变量就能够得以长期存在,有些类似静态变量的意味,而且值得一提的是,这个变量只能够被闭包函数修改,在外部是无法访问和修改的。
*垃圾回收机制:如果某个对象不再被引用,该对象将被回收。
二、闭包内的微观世界
如图所示,函数B的作用域链总共包含了3个部分:函数B的作用域、函数A的作用域和全局Window作用域。
三、闭包的应用
1、循环绑定事件的参数传递
2、设置延时或间隔时候的参数传递
3、用闭包实现程序的暂停执行功能
4、自动执行的匿名函数
这是闭包应用的一个非主流例子,这种形式并非我们通常所谓的“闭包”。我们创建了一个匿名函数并立即执行,由于外部对象无法引用它内部的变量,因此这些变量在函数执行完后很快就会被释放,关键是这种机制不会污染全局对象。
5、实现代码的封装和模块化,实现私有变量
其实Javascript是不能直接实现像C语言中那样的私有成员的,不过在开发的时候,习惯性的统一约定以”_”下划线开头的变量为私有变量。
虽然如此,但是凭借Javascript强大的灵活性,我们完全可以间接的实现私有成员。从上述代码的运行结果可以看出,函数内部的作用域在外部是 无法访问的,但是通过闭包的形式可以访问,从而间接的实现了让person对象具有了私有变量_name,上边的代码还有另外一种表现形式,如下所示:
虽然换了马甲,但是其实还是同样一个事物,只不过在这种表现形式下,闭包显得稍加隐晦,但是闭包还是闭包,它还是那么的强大。
6、实现面向对象中的对象
其实在上边一个例子中我们已经用到了这一特性,但是为了和其它语言中对象的实现形式更加相似,我们将上述代码再次加以改造,如下所示:
四、使用闭包的代价
由于浏览器自身的缺陷,使用闭包时候很可能会造成内存泄露现象,这种现象在IE中尤为突出,内存泄露是一个比较严重的问题,轻则会影响浏览器的响应速度,降低用户体验,重则会造成浏览器无响应等现象。
JavaScript的脚本解释器具备一种垃圾回收机制,一般采用的是引用计数的形式,如果一个对象的引用计数为零,垃圾回收机制就会将其回收,这个过程是自动的。但是一旦当垃圾回收机制碰到了闭包,这个过程就变得复杂起来了。
在闭包中,因为局部变量可能在将来的某些时刻需要被使用,因此垃圾回收机制不会处理这些被外部引用到的局部变量,因此,倘若一旦出现循环引用,就会容易造成内存泄漏。
所谓“闭包(Closure)”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
描述的如此学术的官方解释,相信很少人能够理解,其实所有的函数都是一个闭包,不过我们所说的“闭包”通常指的是函数嵌套所产生的闭包。这种闭包更 为强大,它可以用优雅的方式来处理一些棘手的问题,有些程序员戏谑没有闭包简直就活不下去了,这虽然有些夸张,但从侧面却恰恰说明闭包有着非常强大的功 能。
1 2 3 4 5 6 7 8 9 10 | <script type="text/javascript" language="javascript"> function a(){ var i=0; return function b() { alert(++i); } } c=a(); c(); </script> |
*垃圾回收机制:如果某个对象不再被引用,该对象将被回收。
二、闭包内的微观世界
如图所示,函数B的作用域链总共包含了3个部分:函数B的作用域、函数A的作用域和全局Window作用域。
三、闭包的应用
1、循环绑定事件的参数传递
1 2 3 4 5 6 7 8 9 10 | <ul> <li>list 0</li> <li>list 1</li> <li>list 2</li> <li>list 3</li> </ul> <script type="text/javascript" language="javascript"> function bind_alert(i) { return function() { alert(i); } } (function() { var target=document.getElementsByTagName("li"); for(var i=0;i<target.length;i++) { //target[i].attachEvent('onclick',alert(i)); target[i].attachEvent('onclick',bind_alert(i)); } })(); </script> |
1 2 3 4 5 6 7 8 9 10 | <script type="text/javascript" language="javascript"> function call_later(param1,param2) { return function() { alert("param1 is:"+param1+"\n"); alert("param2 is:"+param2); } } setTimeout(call_later("dierbaby","paulguo"),5000); setInterval(call_later("dierbaby","paulguo"),5000); </script> |
1 2 3 4 5 6 7 8 9 10 | <input type="button" value="Continue!" onclick="test()" /> <script type="text/javascript" language="javascript"> var test = (function() { alert(1); alert(2); return function() { alert(3); alert(4); } })(); </script> |
1 2 3 4 5 6 7 8 | <script> (function(param) { alert(param); var inner="hi"; })("dier"); alert(inner); </script> |
5、实现代码的封装和模块化,实现私有变量
1 2 3 4 5 6 7 8 9 10 | <script type="text/javascript" language="javascript"> var person=function() { var _name="default"; return { getName:function() { return _name; }, setName:function(newName) { _name=newName; } } }(); alert(person._name);//undefined alert(person.getName());//default person.setName("dierbaby"); alert(person.getName());//dierbaby </script> |
虽然如此,但是凭借Javascript强大的灵活性,我们完全可以间接的实现私有成员。从上述代码的运行结果可以看出,函数内部的作用域在外部是 无法访问的,但是通过闭包的形式可以访问,从而间接的实现了让person对象具有了私有变量_name,上边的代码还有另外一种表现形式,如下所示:
1 2 3 4 5 6 7 8 9 10 | <script type="text/javascript" language="javascript"> var person=new function() { var _name="default"; this.getName=function() { return _name; }; this.setName=function(newName) { _name=newName; }; }(); alert(person._name);//undefined alert(person.getName());//default person.setName("dierbaby"); alert(person.getName());//dierbaby </script> |
6、实现面向对象中的对象
其实在上边一个例子中我们已经用到了这一特性,但是为了和其它语言中对象的实现形式更加相似,我们将上述代码再次加以改造,如下所示:
1 2 3 4 5 6 7 8 9 10 | <script type="text/javascript" language="javascript"> function person() { var _name="default"; this.getName=function() { return _name; }; this.setName=function(newName) { _name=newName; }; }; var guokai=new person(); alert(guokai._name);//undefined alert(guokai.getName());//default guokai.setName("guokai"); alert(guokai.getName());//guokai </script> |
由于浏览器自身的缺陷,使用闭包时候很可能会造成内存泄露现象,这种现象在IE中尤为突出,内存泄露是一个比较严重的问题,轻则会影响浏览器的响应速度,降低用户体验,重则会造成浏览器无响应等现象。
JavaScript的脚本解释器具备一种垃圾回收机制,一般采用的是引用计数的形式,如果一个对象的引用计数为零,垃圾回收机制就会将其回收,这个过程是自动的。但是一旦当垃圾回收机制碰到了闭包,这个过程就变得复杂起来了。
在闭包中,因为局部变量可能在将来的某些时刻需要被使用,因此垃圾回收机制不会处理这些被外部引用到的局部变量,因此,倘若一旦出现循环引用,就会容易造成内存泄漏。
相关文章推荐
- javascript学习----window对象的学习与总结
- js
- JSP九大内置对象和四种属性范围解读
- js变量、作用域和内存问题的复习**
- js 省市级联
- JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)
- javascript 面向对象编程
- js的事件流理解
- 学习笔记--原生JS
- JS面向对象组件(五) -- 复制对象(拷贝继承)
- js中constructor的作用
- JS面向对象组件(四) -- 面向对象的继承
- Webpack入门指迷
- JavaScript常用代码(不定时更新)
- JS操作cookie
- JS引擎工作机制描述(转载)
- JavaScript常见的数据类型
- Webpack笔记
- 【翻译】Ext JS最新技巧――2016-3-4
- 【翻译】Ext JS最新技巧——2016-3-4