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

     JavaScript闭包(Closure)

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

      要理解闭包,首先理解两点:变量的作用域以及作用域链,

      例:

        var color = "blue";  
        function changeColor(){     
            var anotherColor = "red";  
            function swapColors(){         
                var tempColor = anotherColor;         
                anotherColor = color;         
                color = tempColor; 
                //在 swapColors函数里面可以访问tempColor,anotherColor和color          
            } 
            //在这里可以访问anotherColor和color,但不能访问tempColor 
            swapColors(); 
        }  

      上面的代码,是用来说明作用域链的概念的,也就是变量从里往外找变量。但是,现在反过来,如果,我们需要在一个外部执行环境里面,访问内部执行环境的变量怎么办呢?

        function f1(){
            var n=999;
            function f2(){
                alert(n); // 999
            }
         f2();
       }
       f1();

      上面的代码很简单,我们在f1里面去调用f2()当然可以弹出n的值999,但是如果我们在外部全局执行环境里面还要获得n的值,这又该怎么办?这就是刚刚的提问,外部的执行环境要求访问内部执行环境的值。

      从外往里访问,这里就可以通过闭包来实现这个效果,把上面的代码稍作修改:

        function f1(){
            var n = 999; //私有变量
    //在函数f1内定义另外的函数作为f1的方法函数 function f2(){ alert(n); //引用外层函数f1的临时变量n } return f2; //返回内部函数 } //调用函数 var result = f1(); result(); // 999

        当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包(示例中,调用函数的时候,result实际调用的是f2函数,也就是说f1的一个内部函数f2在f1之外被调用,这时就创建了一个闭包)。   

        在看一个简单例子:

         function foo() {
            var a = 1;
            function geta() {
                a++;
                return a;
            }
            return geta
        }
        
        myfunc = foo()
        myfunc() // return 2
        myfunc() //return 3

        上面只是一个说明闭包的例子,在实际应用中并不常见。下面我们来看看实际应用中会遇到闭包的情况

          ① 情况一

            界面上有一组a标签,分别点击,希望点击不同的a标签弹出不同的结果,代码如下:

              <ul>
                  <li><a href="#">第0个链接</a></li>
                  <li><a href="#">第1个链接</a></li>
                  <li><a href="#">第2个链接</a></li>
                  <li><a href="#">第3个链接</a></li>
              </ul>    
        
              var links = document.getElementsByTagName("a");
              for(var i=0;i<links.length;i++){
                  links[i].onclick = function(){
                      alert("你现在单击的是第" + i + "个链接");
                  }
              }

          上面这段代码的估计大家都遇到过类似的,想要的效果和实际出现的效果不一致,本来想点击每一个,弹出不同的i的值,但是没想到最后弹出的i的值都是4。

    这正是由于变量的作用域链影响造成的,因为在用户单击a标签的时候才会调用onclick指向的匿名函数。而调用时,需要得到变量i的值,js解析程序首先会在匿名函数内部查找 i ,但是没有定义。于是往外找,在外部执行环境中找到变量 i ,但是这个 i 已经被循环到4了,因为for循环在页面初始化的时候已经被执行了。所以匿名函数里面的i实际上取得的是外部作用域链中i的值。想要改正这个错误,其实就是把i的值传入到匿名函数值就行了。但是匿名函数又不能传值。这个时候,我们就可以用闭包。

    先创建这样一个函数:

            function closureTest(num){
                return function(){
                    alert("你现在单击的是第" + num + "个超链接")
                }
            }

          这个函数直接返回了一个匿名函数,之前讲过闭包,所以,当返回的这个函数在外部被接收的时候,外层函数closureTest里的变量num,就会被保存起来,所以,将之前循环的代码修改一下:

            for(var i = 0;i<links.length;i++){
                links[i].onclick = closureTest(i);
            }

          循环里面onclick调用的函数closureTest,并传入了参数i,而closureTest返回了匿名函数,所以根据闭包的原理,传入的参数i,就被保存在了内存当中,外部访问的就是每次不一样的值了。当然,你也可以直接写成下面这个样子:      

            for(var i=0;i<links.length;i++){
                links[i].onclick = (function(i){
                    return function(){
                        alert("你现在单击的是第" + i + "个链接")
                    }
                })(i);
            }

        ② 情况二

          利用闭包巧妙地传递参数,比如,有这样的场景,点击标签,然后延迟弹出一句话,而而这句话,是用参数传递过去的。先看下面的代码:

            <a href="#">点击我弹出一句话</a>
        
            var link = document.getElementById("myLink");
            link.onclick = function(){
                setTimeout(function(){
                    alert("你点击了点击我的超链接!");
                },1000);
            }

          这段代码,就是点击1秒后,弹出"你点击了点击我的超链接!",当然这里是直接把话写在了function里面,如果这段话是从外面传过来的呢?比如有个方法

            function someFunction(words){
                alert(words);
            }
            var link = document.getElementById("myLink");
            link.onclick = function(){
                setTimeout(someFunction,1000);
            };

          这里的话,问题就来了。怎么往someFunction传递参数呢?这个也可以直接用闭包

            function someFunction(words){
                return function(){
                    alert(words);
                };
            }
            var link = document.getElementById("myLink");
            link.onclick = function(){
                var saySomething = someFunction("你点击了点击我的超链接!");
                setTimeout(saySomething,1000);
            };

          在动态执行环境中,数据实时地发生变化,为了保持这些非持久型变量的值,我们用闭包这种载体来存储这些动态数据。这就是闭包的作用。也就说遇到需要存储动态变化的数据或将被回收的数据时,我们可以通过外面再包裹一层函数形成闭包来解决。

            

  • 相关阅读:
    HTTP 协议中的并发限制及队首阻塞问题
    聊聊JMM
    聊聊CacheLine
    git解决本地建立git仓库 连接远程git仓库出现拒绝合并问题
    django 本地项目部署uwsgi 以及云服务器部署 uwsgi+Nginx+Docker+MySQL主从
    我的第一篇播客
    大爷的超市管理系统——冲刺第一天
    能混绝不C——凡事预则立
    2020软件工程团队作业——05
    2020软件工程作业——04
  • 原文地址:https://www.cnblogs.com/Waiting-for-you/p/4095546.html
Copyright © 2011-2022 走看看