zoukankan      html  css  js  c++  java
  • 深入浅出JavaScript之闭包(Closure)

    闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。下面写下我的学习笔记~

    闭包-无处不在 

    在前端编程中,使用闭包是非常常见的,我们经常有意无意,直接或间接用到了闭包。闭包可以使传递数据更加灵活(比如处理一些点击事件)

    !function() {      
      var localData = "localData here";    
         document.addEventListener('click',    //处理点击事件时用到了外部局部变量,比如这里的localData       
            function(){              
               console.log(localData); 
        }); 
    }(); 

    又比如下面这个例子:(是不是很亲切~~)

    !function() {      
      var localData = "localData here";      
      var url = "http://www.baidu.com/";      
      $.ajax({ 
         url : url,          
         success : function() {              
            // do sth...              
            console.log(localData); 
            } 
        }); 
    }(); 

    再来看一个例子~~这种情况就是我们通常所说的闭包

    function outer() {   
      var localVal = 30;    
      return function(){      
        return localVal;    
      } 
    } 
    var func = outer();  
    func(); // 30

    这个例子中调用outer()返回匿名函数function(),这个匿名函数中可以访问outer()的局部变量localVal,在outer()调用结束后,再次调用func()的时候,仍然能访问到outer()的局部变量localVal

    闭包的概念

    闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量。 --维基百科 

    闭包就是能够读取其他函数内部变量的函数。 --阮一峰

    由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

    所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

    闭包的用途

    这部分转自这篇博文

    闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

    function f1(){
        var n=999;
        nAdd=function(){n+=1}
        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,可以在函数外部对函数内部的局部变量进行操作。

    闭包-封装 

    (function() {   
       var _userId = 23492;   
       var _typeId = 'item';    
       var export = {}; 
        
       function converter(userId) {          
         return +userId; 
       } 
        export.getUserId = function() {         
           return converter(_userId);     
       } 
       export.getTypeId = function() {          
          return _typeId; 
       }         
       window.export = export;   //通过此方式输出
    }());
    
      export.getUserId(); // 23492 
      export.getTypeId();  // item 
      export._userId;    // undefined  
      export._typeId;    // undefined       
      export.converter; // undefined

    利用闭包的特性能让我们封装一些复杂的函数逻辑,在这个例子中调用export上的方法(getUserId,getTypeId)间接访问函数里私有变量,但是直接调用export._userId是没法拿到_userId的。这也是Node里面常用到特性吧~

    常见错误之循环闭包 

    下面这个案例,我们添加3个div,值分别为aaa,bbb,ccc,我们想实现的是点击aaa输出1,点击bbb输出2,点击ccc输出3

    document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>";  
    for (var i = 1; i < 4; i++) {      
      document.getElementById('div' + i).         
        addEventListener('click', function() {         
        alert(i); // all are 4! 
        });  
    }

    结果点击aaa,bbb还是ccc都是alert(4)~~

    产生这样的问题在于这个i的值在初始化完成的时候就已经是4了

    要达到我们想要的点击aaa输出1,点击bbb输出2,点击ccc输出3,要用到闭包的技巧,在每次循环的时候,用立即执行的匿名函数把它包装起来,这样子做的话,每次alert(i)的值就取自闭包环境中的i,这个i来自每次循环的赋值i就能输出1,2,3了

    document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>";  
    for (var i = 1; i < 4; i++) {
      !function(i){ //②再用这个参数i,到getElementById()中引用     
        document.getElementById('div' + i).       
          addEventListener('click', function() {         
          alert(i); // 1,2,3
         });  
      }(i);  //①把遍历的1,2,3的值传到匿名函数里面
    } 

    思考题

    如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。(来自阮老师)这题目总结得真秒~~

    代码片段一。

      var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
          return function(){
            return this.name;
          };
        }
      };
      alert(object.getNameFunc()());

    代码片段二。

      var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
          var that = this;
          return function(){
            return that.name;
          };
        }
      };
      alert(object.getNameFunc()());
  • 相关阅读:
    Maven 集成Tomcat插件
    dubbo 序列化 问题 属性值 丢失 ArrayList 解决
    docker 中安装 FastDFS 总结
    docker 从容器中拷文件到宿主机器中
    db2 相关命令
    Webphere WAS 启动
    CKEDITOR 4.6.X 版本 插件 弹出对话框 Dialog中 表格 Table 自定义样式Style 问题
    SpringMVC JSONP JSON支持
    CKEDITOR 3.4.2中 按钮事件中 动态改变图标和title 获取按钮
    git回退到远程某个版本
  • 原文地址:https://www.cnblogs.com/MuYunyun/p/5930703.html
Copyright © 2011-2022 走看看