JavaScript的函数闭包概念

闭包是指有权访问另一个函数作用域中的变量的函数

一、创建闭包的常见的方式:

    就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。

   //通过闭包可以返回局部变量
    function box() {
        var user = ‘Lee‘;
        return function () { //通过匿名函数返回 box()局部变量
            return user;
        };
    }
    alert(box()()); //通过box()()来直接调用匿名函数返回值

    var b = box();
    alert(b()); //另一种调用匿名函数返回值

二、闭包的优点就是缺点:

    就是可以把局部变量驻留在内存中,可以避免使用全局变量。(全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难,所以推荐使用私有的,封装的局部变量)。

   //通过全局变量来累加
    var age = 100;                 //全局变量
    function box() {
        age ++;                 //模块级可以调用全局变量,进行累加
    }
    box();                         //执行函数,累加了
    alert(age);                 //输出全局变量,结果是101
    box();
    alert(age);                    //结果是102,累加了
  //通过局部变量无法实现累加
    function box() {
        var age = 100;
        age ++;                     //累加
        return age;
    }
    alert(box()); //101
    alert(box()); //101,无法实现,因为又被初始化了
   //通过闭包可以实现局部变量的累加
    function box() {
        var age = 100;
        return function () {
            age ++;
            return age;
        }
    }
    var b = box(); //获得函数 当不用的时候将b解除引用b=null,等待垃圾回收
    alert(b()); //调用匿名函数
    alert(b()); //第二次调用匿名函数,实现累加

    由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
    在使用完闭包后将b解除引用,这样就不会一直占据着内存了

三、闭包的问题:循环里包含匿名函数

    1、作用域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值。

 //循环里包含匿名函数
    function box() {
        var arr = [];
        for (var i = 0; i < 5; i++) {
            arr[i] = function () {
                return i;
            };
        }
        return arr;
    }

    var b = box(); //调用这个函数,返回一个数组,数组中的元素是循环里的匿名函数本身
    alert(b.length); //得到的数组长度结果是5

    for (var i = 0; i < b.length; i++) {
        alert(b[i]()); //输出每个函数的值,都是最后一个值(即循环里执行到最后一步的i++,是5)
    }

      上面的例子输出的结果都是 5,也就是循环后得到的最大的 i 值。

      因为 b[i]调用的是匿名函数,匿名函数并没有自我执行,等到调用的时候,box()已执行完毕返回一个数组了,循环也已经结束了所以,i 早已变成 5,最终的结果就是 5 个 5。

    2、改进上面的列子

      改1:循环里包含匿名函数-改 1,自我执行匿名函数

  function box() {
        var arr = [];
        for (var i = 0; i < 5; i++) {
            arr[i] = (function (num) {                 //通过自我及时执行
                return num;
            })(i);                                         //并且传参
        }
        return arr;
    }
    var b = box();//返回的是数组,数组里的元素不是函数了

    for (var i = 0; i < b.length; i++) {
        alert(b[i]);                                     //这里返回的是数组,直接打印即可
    }

        我们让匿名函数进行自我执行,导致最终返回给 a[i]的是数组而不是函数了。最终导致 b[0]-b[4]中保留了 0,1,2,3,4 的值。

      改进2:循环里包含匿名函数-改 2,匿名函数下再做个匿名函数

  function box() {
        var arr = [];
        for (var i = 0; i < 5; i++) {
            arr[i] = (function (num) {
                //num其实在这里
                return function () {             //直接返回值,改 2 变成返回函数
                    return num;                 //原理和改 1 一样
                }
            })(i);
        }
        return arr;
    }

    var b = box();
    alert(b);
    for (var i = 0; i < b.length; i++) {
        alert(b[i]());                     //这里通过 b[i]()函数调用即可
    }
    //box()函数已经执行完了,为什么还能是0,1,2,3,4呢?  因为闭包可以将变量驻留在内存中和上述的累加是一样的道理

    改 1 和改 2 中,我们通过匿名函数自我执行,立即把结果赋值给 a[i]。每一个 i,是调用方通过按值传递的,所以最终返回的都是指定的递增的 i。而不是 box()函数里的 i。

四、闭包中的this使用问题

    在闭包中使用 this 对象也可能会导致一些问题,this 对象是在运行时基于函数的执行环境绑定的,如果 this 在全局范围就是 window,如果在对象内部就指向这个对象。

    而闭包却在运行时指向 window 的,因为闭包并不属于这个对象的属性或方法。

  var user = ‘The Window‘;
    var obj = {
        user : ‘The Object‘,
        getUserFunction : function () {
            return function () { //闭包不属于obj,里面的this指向window
                return this.user;
            };
        }
    };

    alert(obj.getUserFunction()()); //The window

    使用对象冒充:可以强制指向某个对象

       var user = ‘The Window‘;
      var obj = {
          user : ‘The Object‘,
          getUserFunction : function () {
              return function () { //闭包不属于obj,里面的this指向window
                  return this.user;
              };
          }
      };

    alert(obj.getUserFunction()()); //The window
    alert(obj.getUserFunction().call(obj)); //The Object    

    也可以从上一个作用域中得到对象

       var user = ‘The Window‘;
    var obj = {
        user : ‘The Object‘,
        getUserFunction : function () {
            //这里作用域的this是obj
            var that = this;//将这里的作用域交给that
            return function () {
                //这里作用域的this是window
                return that.user;
            };
        }
    };

    alert(obj.getUserFunction()()); //The Object

五、内存泄漏(这是在IE6,7,8中的问题)

    由于 IE 的 JScript 对象和 DOM 对象使用不同的垃圾收集方式,因此闭包在 IE 中会导致一些问题。就是内存泄漏的问题,也就是无法销毁驻留在内存中的元素。

<body>
    <div id="oDiv">Lee</div>
</body>
</html>
<script type="text/javascript">
    function box() {
        var oDiv = document.getElementById(‘oDiv‘); //oDiv 用完之后一直驻留在内存
        oDiv.onclick = function () {
            alert(oDiv.innerHTML);           //这里用 oDiv 导致内存泄漏
        };
    }
    box();
</script>

    那么在最后应该将 oDiv 解除引用来避免内存泄漏。

<body>
    <div id="oDiv">Lee</div>
</body>
</html>
<script type="text/javascript">
    function box() {
        var oDiv = document.getElementById(‘oDiv‘);
        var text = oDiv.innerHTML;
        oDiv.onclick = function () {
            alert(text);
        };
        oDiv = null;       //解除引用,等待垃圾回收
        alert(oDiv);       //null,说明已经不存在了
    }
    box();
</script>
时间: 2024-10-13 01:37:31

JavaScript的函数闭包概念的相关文章

JavaScript碎片———函数闭包(模拟面向对象)

经过这几天的博客浏览,让我见识大涨,其中有一篇让我感触犹深,JavaScript语言本身是没有面向对象的,但是那些大神们却深深的模拟出来了面向对象,让我震撼不已.本篇博客就是在此基础上加上自己的认知,如有错误,还请见谅. 具体来说实现模拟面向对象主要是利用JavaScript函数闭包这个概念.由于JavaScript中的每一个function都会形成一个作用域,而如果变量声明在这个域中,那么外部是无法直接去访问,要想访问必须new出一个实例来,相当于Java中class.首先让我们来了解一下pr

JavaScript的函数闭包使用

一.模仿块级作用域 JavaScript 没有块级作用域的概念,那么可以模拟像java中将很多变量私有化封装起来,保护数据,防止数据泄漏,封装细节,这样安全性和可控性更高 function box(count) { for (var i=0; i<count; i++) { //块级作用域(JS中没有这个东西) } alert(i); //i 不会因为离开了 for 块就失效 } box(2); //结果是2,还是可以访问的 function box(count) { for (var i=0;

javascript匿名函数 闭包

匿名函数 (function(){                console.info("111111111");            })(); var my = (function(){                return "11111111";            })(); 函数里放匿名函数叫闭包 function my(){                return function(){                    retur

javaScript——作用域和闭包概念

js是函数级别作用域,在内部的变量,内部能都访问到,外部不能访问内部的,内部的可以访问外部的 闭包就是,拿到本不是应该属于他的东西. 当在函数内部定义了其他函数时,就创建了闭包,闭包有权访问包含函数内部的所有变量,原理如下: 1:在后台执行环境中,闭包的作用域链包含着他自己的作用域,包含函数的作用域和全局作用域. 2:通常,函数的作用域以及所有变量都会在函数执行结束后销毁. 3:但是,当函数返回了一个闭包的时候,这个函数的作用域就会一直再内存中保存,知道闭包不存在为止.

javascript系列2 -- 闭包详解

转发请标明来源:http://www.cnblogs.com/johnhou/p/javascript.html  请尊重笔者的劳动成果  --John Hou 今天我们从内存结构上来讲解下 javascript中的闭包概念. 闭包:是指有权访问另外一个函数作用域中的变量的函数.创建闭包的常见方式就是在一个函数内部创建另外一个函数. 在javascript中没有块级作用域,一般为了给某个函数申明一些只有该函数才能使用的局部变量时,我们就会用到闭包,这样我们可以很大程度上减少全局作用域中的变量,净

静态属性,函数闭包,call/apply,继承

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>静态属性,函数闭包,call/apply,继承</title> <script type="text/javascript"> /* 一:静态属性 在一些面向对象语言里,可以使用static关键字来显示的定义属性和方法.这一点

转:深入理解JavaScript闭包概念

闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感觉,事实上,闭包的概念在函数式编程语言中算不上是难以理解的知识.如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感. 在DOM的事件处理方面,大多数程序员甚至自己已经在使用闭包了而不自知,在这种情况下,对于浏览器中内嵌的JavaScript引擎的bug可能造成内存泄漏这一问题姑且不论,就是程序员自己调试也常常会一头雾水. 用简单的语句来描述JavaScript中的闭

深入理解Javascript闭包概念

一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数

JavaScript基础学习之-javascript权威指南--8.6函数闭包

一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 相信很少有人能直接看懂这句话,因为他描述的太学术. 其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”.看下面这段代码: function a() { var i = 0; function b() { alert(++i); } r