JavaScript闭包研究

来源:互联网 发布:英美杂志软件 编辑:程序博客网 时间:2024/06/01 10:23

           林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
           摘要:本文主要讲了JavaScript闭包的原理、特点并用一些实例做了证明

一、闭包的概念与好处

1.1、什么是闭包?
闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包的特点:
  1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
  简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

闭包:是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另外一个函数

1.2、javascript的垃圾回收原理

1.在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
2.如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

1.3、使用闭包的好处

那么使用闭包有什么好处呢?使用闭包的好处是:
1.希望一个变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在

二、实例讲解

内部函数

让我们从一些基础的知识谈起,首先了解一下内部函数。内部函数就是定义在另一个函数中的函数。例如:

function outerFn () {    functioninnerFn () {}}
innerFn就是一个被包在outerFn作用域中的内部函数。这意味着,在outerFn内部调用innerFn是有效的,而在outerFn外部调用innerFn则是无效的。

当function里嵌套function时,内部的function可以访问外部function里的变量。

如下:

<!DOCTYPE html><meta charset="utf-8" /> <html><head></head><body>    <button onclick="fun()">请点击这里</button>    <script>        function fun()                    {                   outfun(1);                    }   function outfun(x) {             var tmp = 1;             function infun(y) {             alert(x + y + (++tmp));            }            infun(10);         }     </script></body></html>

输出结果;


不管执行多少次,都会alert 13,因为infun能访问outfun的参数x,也能访问outfun的变量tmp。但,这还不是闭包。当你return的是内部function时,就是一个闭包。内部function会close-over外部function的变量直到内部function结束。

将函数更改成如下就是闭包了:

<!DOCTYPE html><meta charset="utf-8" /> <html><head></head><body>    <button onclick="fun()">请点击这里</button>    <script>    var test =  outfun(1);        function fun()                   {               test(10);                    }   function outfun(x) {             var tmp = 1;            return  function infun(y) {             alert(x + y + (++tmp));            } //这样才是闭包        }     </script></body></html>

这里每次点击后结果都会加1

第一次点:


第二次点:



当然, 我们也可以使用全局变量来调用闭包

再来看一个例子

 function f1(){    var n=999;    nAdd=function(){n+=1}//所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去    function f2(){      alert(n);    }    return f2;  }  var result=f1();  result(); // 999  nAdd();  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,可以在函数外部对函数内部的局部变量进行操作。

JavaScript中所有的function都是一个闭包。不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。看下面这段代码:

function a() { var i = 0; function b() { alert(++i); } return b;}var c = a();c();

这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

看完了上面,闭包其实在json中也可以来使用,这种方法在很多框架中还经常见到,最基础如下:

<script>var aaa = (function(){        var a = 1;        function bbb(){                a++;                alert(a);        }        function ccc(){                a++;                alert(a);        }        return {                b:bbb,             //json结构                c:ccc        }})();aaa.b();     //2aaa.c()      //3</script>

这里使用了json,返回是一个json

我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,

那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

var CachedSearchBox = (function(){ var cache = {}, count = []; return { attachSearchBox : function(dsid){ if(dsid in cache){//如果结果在缓存中 return cache[dsid];//直接返回缓存中的对象 } var fsb = new uikit.webctrl.SearchBox(dsid);//新建 cache[dsid] = fsb;//更新缓存 if(count.length > 100){//保正缓存的大小<=100 delete cache[count.shift()]; } return fsb; }, clearSearchBox : function(dsid){ if(dsid in cache){ cache[dsid].clearSelection(); } } }; })(); CachedSearchBox.attachSearchBox("input"); 
这样我们在第二次调用的时候,就会从缓存中读取到该对象,如果上面的例子看不懂,那就看下面的吧

var db = (function() {// 创建一个隐藏的object, 这个object持有一些数据// 从外部是不能访问这个object的var data = {};// 创建一个函数, 这个函数提供一些访问data的数据的方法return function(key, val) {    if (val === undefined) { return data[key] } // get    else { return data[key] = val } // set    }// 我们可以调用这个匿名方法// 返回这个内部函数,它是一个闭包})();db('x'); // 返回 undefineddb('x', 1); // 设置data['x']为1db('x'); // 返回 1// 我们不可能访问data这个object本身// 但是我们可以设置它的成员


三、使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便
改变父函数内部变量的值。

7 0