当前位置:文档之家› 详谈JavaScript 匿名函数及闭包

详谈JavaScript 匿名函数及闭包

详谈JavaScript 匿名函数及闭包1、匿名函数函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途。

匿名函数:就是没有函数名的函数。

1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式第一种:这也是最常规的一种代码如下:function double(x){return 2 * x;}第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。

代码如下:var double = new Function('x', 'return 2 * x;');第三种:var double = function(x) { return 2* x; }注意“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量square。

1.2 匿名函数的创建第一种方式:就是上面所讲的定义square函数,这也是最常用的方式之一。

第二种方式:代码如下:(function(x, y){alert(x + y);})(2, 3);这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

2、闭包闭包的英文单词是closure,这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。

闭包的含义:闭包说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕(这点涉及JavaScript作用域链)。

示例一代码如下:function checkClosure(){var str = 'rain-man';setTimeout(function(){ alert(str); } //这是一个匿名函数, 2000);}checkClosure();这个例子看上去十分的简单,仔细分析下它的执行过程还是有许多知识点的:checkClosure 函数的执行是瞬间的(也许用时只是0.00001毫秒),在checkClosure的函数体内创建了一个变量str,在checkClosure执行完毕之后str并没有被释放,这是因为setTimeout内的匿名函数存在这对str的引用。

待到2秒后函数体内的匿名函数被执行完毕,str才被释放。

示例二,优化代码代码如下:function forTimeout(x, y){alert(x + y);}function delay(x , y , time){setTimeout('forTimeout(' + x + ',' + y + ')' , time);}/*** 上面的delay函数十分难以阅读,也不容易编写,但如果使用闭包就可以让代码更加清晰* function delay(x , y , time){* setTimeout(* function(){* forTimeout(x , y)* }* , time);* }3、举例匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。

示例三:代码如下:var oEvent = {};(function(){var addEvent = function(){ /*代码的实现省略了*/ };function removeEvent(){}oEvent.addEvent = addEvent;oEvent.removeEvent = removeEvent;})();在这段代码中函数addEvent和removeEvent都是局部变量,但我们可以通过全局变量oEvent 使用它,这就大大减少了全局变量的使用,增强了网页的安全性。

我们要想使用此段代码:oEvent.addEvent(document.getElementById('box') , 'click' , function(){});示例四:代码如下:var rainman = (function(x , y){return x + y;})(2 , 3);/*** 也可以写成下面的形式,因为第一个括号只是帮助我们阅读,但是不推荐使用下面这种书写格式。

* var rainman = function(x , y){* return x + y;* }(2 , 3);*/在这里我们创建了一个变量rainman,并通过直接调用匿名函数初始化为5,这种小技巧有时十分实用。

示例五:代码如下:var outer = null;(function(){var one = 1;function inner (){one += 1;alert(one);}outer = inner;})();outer(); //2outer(); //3outer(); //4这段代码中的变量one是一个局部变量(因为它被定义在一个函数之内),因此外部是不可以访问的。

但是这里我们创建了inner函数,inner函数是可以访问变量one的;又将全局变量outer引用了inner,所以三次调用outer会弹出递增的结果。

4、注意4.1 闭包允许内层函数引用父函数中的变量,但是该变量是最终值示例六:代码如下:/*** <body>* <ul>* <li>one</li>* <li>two</li>* <li>three</li>* <li>one</li>* </ul>*/var lists = document.getElementsByTagName('li');for(var i = 0 , len = lists.length ; i < len ; i++){lists[ i ].onmouseover = function(){alert(i);};}你会发现当鼠标移过每一个<li&rt;元素时,总是弹出4,而不是我们期待的元素下标。

这是为什么呢?注意事项里已经讲了(最终值)。

显然这种解释过于简单,当mouseover事件调用监听函数时,首先在匿名函数(function(){ alert(i); })内部查找是否定义了i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。

解决方法一:代码如下:var lists = document.getElementsByTagName('li');for(var i = 0 , len = lists.length ; i < len ; i++){(function(index){lists[ index ].onmouseover = function(){alert(index);};})(i);}解决方法二:代码如下:var lists = document.getElementsByTagName('li');for(var i = 0, len = lists.length; i < len; i++){lists[ i ].$$index = i; //通过在Dom元素上绑定$$index属性记录下标lists[ i ].onmouseover = function(){alert(this.$$index);};}解决方法三:代码如下:function eventListener(list, index){list.onmouseover = function(){alert(index);};}var lists = document.getElementsByTagName('li');for(var i = 0 , len = lists.length ; i < len ; i++){eventListener(lists[ i ] , i);}4.2 内存泄露使用闭包十分容易造成浏览器的内存泄露,严重情况下会是浏览器挂死。

可以参考下面内容预防:JavaScript垃圾回收机制JavaScript不需要手动地释放内存,它使用一种自动垃圾回收机制(garbage collection)。

当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量。

复制代码代码如下:var s = [ 1, 2 ,3];var s = null;//这样原始的数组[1 ,2 ,3]就会被释放掉了。

3、循环引用三个对象A 、B 、CAàBàC :A的某一属性引用着B,同样C也被B的属性引用着。

如果将A清除,那么B、C也被释放。

AàBàCàB :这里增加了C的某一属性引用B对象,如果这是清除A,那么B、C不会被释放,因为B和C之间产生了循环引用。

复制代码代码如下:var a = {};a.pro = { a:100 };a.pro.pro = { b:100 };a = null ;//这种情况下,{a:100}和{b:100}就同时也被释放了。

var obj = {};obj.pro = { a : 100 };obj.pro.pro = { b : 200 };var two = obj.pro.pro;obj = null;//这种情况下{b:200}不会被释放掉,而{a:100}被释放了。

4、循环引用和闭包复制代码代码如下:function outer(){var obj = {};function inner(){//这里引用了obj对象}obj.inner = inner;}这是一种及其隐蔽的循环引用,。

当调用一次outer时,就会在其内部创建obj和inner两个对象,obj的inner属性引用了inner;同样inner也引用了obj,这是因为obj仍然在inn erFun的封闭环境中,准确的讲这是由于JavaScript特有的“作用域链”。

因此,闭包非常容易创建循环引用,幸运的是JavaScript能够很好的处理这种循环引用。

5、IE中的内存泄漏IE中的内存泄漏有好几种,这里有详细的解释(/en-us/libra ry/bb250448.aspx)。

这里只讨论其中一种,即循环引用所造成的内存泄漏,因为,这是一种最普遍的情况。

当在DOM元素或一个ActiveX对象与普通JavaScript对象之间存在循环引用时,IE在释放这类变量时存在特殊的困难,最好手动切断循环引用,这个bug在IE 7中已经被修复了(/blog/archives/2006/04/ie_7_and_javasc.html)。

相关主题