JavaScript之闭包

更新日期: 2020-05-01阅读: 2.1k标签: 闭包

闭包(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()());

    链接: https://www.fly63.com/article/detial/8928

    前端开发闭包理解,JavaScript-闭包

    闭包(closure)是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠...

    使用闭包的方式实现一个累加函数 addNum

    使用闭包的方式实现一个累加函数 addNum,参数为 number 类型,每次返回的结果 = 上一次计算的值 + 传入的值

    javascript中闭包最简单的介绍

    JavaScript 变量可以是局部变量或全局变量。私有变量可以用到闭包。闭包就是将函数内部和函数外部连接起来的一座桥梁。函数的闭包使用场景:比如我们想要一个函数来执行计数功能。

    js循环中的异步&&循环中的闭包

    for循环中let 和var的区别,setTimeout(func,time)函数运行机制,一个需求,一个数组array[1,2,3,4,5],循环打印,间隔1秒

    深入了解JavaScript的闭包机制

    这篇文章主要介绍了JavaScript的闭包机制,针对内嵌函数的变量访问等问题分析了JS的闭包,写的十分的全面细致,具有一定的参考价值,JavaScript 变量可以是局部变量或全局变量。 私有变量可以用到闭包。

    JavaScript 作用域、命名空间及闭包

    变量作用域:一个变量的作用域是程序源代码中定义这个变量的区域,在函数内声明的变量是局部变量,它只在该函数及其嵌套作用域里可见(js 函数可嵌套定义);

    Js中的闭包

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

    js闭包问题

    使用闭包能够让局部变量模拟全局变量一样,但是它只能被特定函数使用。我们都知道:1.全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性,这样保护变量的安全

    JS之闭包的定义及作用

    在学习闭包之前,你必须清楚知道JS中变量的作用域。JS中变量的作用域无非就全局变量和局部变量,两者之间的关系是函数内部可以直接访问全局变量,但是函数外部是无法读取函数内部的局部变量的

    Js函数高级-闭包

    JavaScript的运行机制:(1)所有同步任务都在主线程上执行,形成一个执行栈。(2)主线程之外,还有一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。

    点击更多...

    内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!