zoukankan      html  css  js  c++  java
  • JavaScript闭包

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

    闭包的局部变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。

    在ECMAScript中,函数对象中定义的 内部函数(inner function) 是可以直接访问外部函数的局部变量,创建闭包的常见方式,就是在一个函数内部创建另一个函数。

    function welcome (name) {
         var text = 'Hello ' + name;   // local variable
         // 每次调用时,产生闭包,并返回内部函数对象给调用者
         return function () {
             console.log(text);
         }
    }
    var sayHello = welcome( "Viewer" );
    sayHello()   // 通过闭包访问到了局部变量text

    代码的执行结果是:Hello Viewer,因为 sayHello() 函数在 welcome() 函数执行完毕后,仍然可以访问到定义在其内部的局部变量 text ,这就是闭包的效果。

    在ECMAscript的脚本的函数运行时,每个函数关联都有一个执行上下文场景(Execution Context) ,这个执行上下文场景中包含三个部分:

    • 文法环境(The LexicalEnvironment)
      • 环境记录(Enviroment Recode)
      • 外部引用(指针)
    • 变量环境(The VariableEnvironment)
      • 局部变量
      • 参数变量
    • this绑定

    外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为 NULL。这样的数据结构就构成了一个单向的链表,每个引用都指向外层的上下文场景。

    例如上面我们例子的闭包模型应该是这样,sayHello 函数在最下层,上层是函数 welcome,最外层是全局场景。如下图:

    因此当sayHello被调用的时候,sayHello会通过上下文场景找到局部变量text的值,因此在屏幕的对话框中显示出”Hello Viewer”。

    变量环境(The VariableEnvironment)和文法环境的作用基本相似。

    闭包实例

    前面的内容大致说明了Javascript闭包是什么,闭包在Javascript如何实现,下面通过针对一些例子来深入了解闭包。

    例子1:闭包中局部变量是引用而非复制

    function say667() {
        var num = 666;
        var sayAlert = function() {
            console.log(num);
        }
        num++;
        return sayAlert;
    }
      
    var sayAlert = say667();
    sayAlert()    //667

    例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

    function setupSomeGlobals () {
        var num = 666;
    
        // 存储一些函数的引用作为全局变量
        gAlertNumber = function() {
            console.log(num);
        }
    
        gIncreaseNumber = function() {
            num++;
        }
    
        gSetNumber = function(x) {
            num = x;
        }
    }
    
    setupSomeGlobals();     // 为三个全局变量赋值
    gAlertNumber();         //666
    
    gIncreaseNumber();
    gAlertNumber();         // 667
    
    gSetNumber(12);         
    gAlertNumber();         //12

    例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

    <script type="text/javascript">
      window.onload = function(){
        var lists = document.getElementsByTagName("li");
        for(var i=0,l=lists.length; i < l; i++){
          lists[i].onclick = function(){
            var t = i;
            return function(){
              console.log(t+1)
            }
          }()
        }
      }
    </script>
    
    <body>
    <h1>当在一个循环中赋值函数时,这些函数将绑定同样的闭包</h1>
    <ul>
      <li id="a1">aa</li>
      <li id="a2">aa</li>
      <li id="a3">aa</li>
    </ul>
    <body>

    例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

    function sayAlice() {
        var sayAlert = function() {
            console.log(alice);
        }
    
        var alice = 'Hello Alice';
        return sayAlert;
    }
    var helloAlice = sayAlice();
    helloAlice();

    例子5:每次函数调用的时候创建一个新的闭包。

    function newClosure(someNum, someRef) {
        var num = someNum;
        var anArray = [1,2,3];
        var ref = someRef;
        return function(x) {
            num += x;
            anArray.push(num);
            console.log('num: ' + num +
            '\nanArray ' + anArray.toString() +
            '\nref.someVar ' + ref.someVar);
        }
    }
    closure1 = newClosure(40,{someVar:'closure 1'});
    closure2 = newClosure(1000,{someVar:'closure 2'});
      
    closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
    closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'

    由于闭包会携带包含它的函数的作用域,因此会比其它函数占用更多的内存,过度使用闭包会导致占用内存,所以只有在必要的时候再使用闭包。

    参考资料:http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/

                  http://coolshell.cn/articles/6731.html

  • 相关阅读:
    FPGA市场潜力有几多?
    FPGA前世今生(四)
    FPGA前世今生(三)
    FPGA前世今生(二)
    FPGA前世今生(一)
    嵌入式视频处理考虑(二)
    常用Linux操作命令
    混合开发学习路线
    Eclipse使用
    ECS的配置与使用
  • 原文地址:https://www.cnblogs.com/zhoufulin/p/4973129.html
Copyright © 2011-2022 走看看