阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      JavaScript 闭包的使用场景

      TODO 闭包的特性问题??????

      在什么场合下用

      • 1、保护变量;用自运行的匿名函数来实现;
      • 2、协调异步代码;自运行的匿名函数来实现,并且传参进去(异步代码需要用到原来的变量,通过作为函数的参数传进去;);

        实例

      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
      </head>
      <body>
      <div id="div1">
        <p>0000</p>
        <p>1111</p>
        <p>2222</p>
        <p>3333</p>
        <p>4444</p>
      </div>
      <script>
      var oDiv = document.getElementById("div1");
      var oPs = oDiv.getElementsByTagName("p");
      for (i = 0; i < oPs.length; i++) {
        ;(function (i){
          oPs[i].onclick = function () {
            console.log(i);
          }
        })(i);
      }
      </script>
      </body>
      </html>
      

      上面的也可以改造成这样的;[也是闭包的应用]

      for (i = 0; i < oPs.length; i++) {
          oPs[i].onclick = function (i) {
              return function(){console.log(i);}
          }(i)
      }
      

      高程三上说一个函数里面返回一个函数,这样是才叫闭包;(个人认为闭包是一种机制;是一个封闭的作用域;只有形成封闭作用域都可以叫做闭包的);上面就是高程上说的标准闭包写法;

      但是闭包的性能其实没有自定义属性好;可以用自定义属性来写;下面是用自定义属性的写法来做的;

      for (i = 0; i < oPs.length; i++) {
          oPs[i].index=i;
          oPs[i].onclick = function () {
              console.log(this.index);
          }
      }
      

      关于作用域/闭包的其它理解;

      var name = "china";
      var age = 5000;
      (function (name, age) {//形参变量也是私有作用域中的私有变量
        name = "zhu";
        age = 17;
        alert("zz");
        console.log(name, age);
      })(name, age);//执行函数传递参数值,它只是把全局的name和age的值分别的赋值给我们的形参 ->"执行的时候传递的永远是一个值"
      alert("22")
      console.log(name, age);//先执行自执行函数,然后再执行此行
      

      在函数里面修改全局的name和age(前提是私有的name和age依然存在)

      var name = "china";
      var age = 5000;
      (function (name, age) {
          name = "zhu";
          age = 17;
          //把全局的也修改为"zhu"、7 ->在私有作用域中通过指定前缀来强行的修改的全局下的值
          window.name = name;
          window.age = age;
      })(name, age);
      console.log(name, age);//zhu ,17 
      

      jQuery部分实现的原理:在私有中定义jQuery,通过window.xxx让其在全局下也可以使用

      (function () {
          var jQuery = function () {
            console.log("核心代码");
          };
          window.jQuery = window.$ = jQuery;
      })();
      jQuery();
      $();
      

      闭包用在选项卡等效果上

          var banner = document.getElementById("banner");
          var tip = document.getElementById("tip");
          var oDivs = banner.getElementsByTagName("div");
          var oLis = tip.getElementsByTagName("li");
          //鼠标滑过某一个li,我们让所有的li和所有的div都没有选中的样式(select),然后让当前选中的这个li和对应的div有选中的样式
      	
          function changeTab(nIndex) {
            for (var i = 0; i < oLis.length; i++) {
             oLis[i].className = null;
             oDivs[i].className = null;
            }
            oLis[nIndex].className = "select";
            oDivs[nIndex].className = "select";
          }
      

      1、传统的循环绑定事件的方式

      for (var i = 0; i < oLis.length; i++) {
          oLis[i].onmouseover = function () {
              changeTab(i);
          };
      }
      

        > 这种方式不能实现我们的效果,为什么?   > 我们是循环所有的li,给每一个li都绑定了鼠标滑过的事件->

      /*
      i=0
      oLis[0].onmouseover=function(){changeTab(i);}; 我们绑定事件其实是把匿名函数定义的主体赋值给元素的事件行为 -> oLis[0].onmouseover=xxxfff000;
      i=1
      oLis[1].onmouseover=function(){changeTab(i);};
      ...
      i=4
      oLis[4].onmouseover=function(){changeTab(i);};
      i=5
      循环结束
      */
      

      当我们手动触发的这些行为的时候,比如触发第一个li的onmouseover,执行第一个绑定的方法:行程一个私有的作用域->形参赋值->预解释->代码执行,遇到了一个变量i,不是自己的私有的,找全局作用域下的i,这个i的值已经变为循环结束后的5,所以不是我们期望的索引值…

      2、如何解决这个问题?

      [作用域式解决]

      当触发onmouseover的时候,全局的i变为了5,我们找全局的肯定不行,但是我可一这样来操作:

      在当前私有作用域和全局作用域之间在嵌入一层作用域A,然后让A中有一个私有的变量i,存储的是自己的索引即可

      for (var i = 0; i < oLis.length; i++) {
          (function (i) {
              oLis[i].onmouseover = function () {
                  changeTab(i);
              };
          })(i);//每一次循环把当前i的值(0,1,2,3,4)赋值给形参i
      }
      

      函数执行形成不销毁的私有的作用域的第二种情况:

      在一个函数中,给函数外面的DOM元素绑定事件 -> 由于给事件绑定的方法是一个引用数据类型值,而元素不属于这个作用域,而是函数外面的变量,这样也相当于外面的东西占用了函数里面的内存,当前的私有作用域不销毁

      for (var i = 0; i < oLis.length; i++) {
          oLis[i].onmouseover = function (i) {
              return function () {
                  changeTab(i);
              };
          }(i);
      }
      

      弊端:会形成多个不销毁的私有的作用域,占内存

      [自定义属性的方式]

      自定义属性处理问题是JS中最常用的编程思想

      for (var i = 0; i < oLis.length; i++) {
          oLis[i].index = i;//每一次循环把当前li的索引存储到自己的自定义属性index上,后期需要用到索引的时候我们只需要把自定义属性index的值获取到拿来用即可
          oLis[i].onmouseover = function () {
              //this->当前鼠标滑过的这个li
              changeTab(this.index);
          };
      }
      

      案例2:用setTimeout实现把p相对上一个p向右移动100px;1秒中只实现一个P的移动;

      setTimeout本身是同步的代码,但是setTimeout里面的函数是异步的代码;setTimeout告诉浏览器隔多少秒执行里面的函数;这个函数是脱离执行顺序的;属于异步代码; 代码如下:

      var oDiv = document.getElementById("div1");
      var oPs = oDiv.getElementsByTagName("p");
      for (i = 0; i < oPs.length; i++) {
          (function(i){
              setTimeout(function(){
                  oPs[i].style.left=100*i+"px";
              },i*1000)
          })(i);
      }
      

      因为定时器里面的函数是异步的,而setTimeout本身是同步的;所以i和里面的i不一致;用一个闭包把他们包起来;然后传i进去;或者直接包住setTimeout里面的函数;

      setTimeout((function(i){
          return function(){oPs[i].style.left=100*i+"px";}
      })(i),i*1000);
      
      目录
      目录