zoukankan      html  css  js  c++  java
  • 关于闭包的几点认识

    什么是闭包?

    闭包一般人都说, 是函数中定义的一个函数,有的人也说函数中套函数。 

    其实准确点说,闭包是一个能够访问到其他函数内部变量的函数,

    当然这个其他函数不是父子关系的函数, 而是兄弟关系的函数。


    举个最简单的例子引导一下

    function alwaysLinger(){
        var num = 0;

    // 一堆一堆逻辑 }
    console.log( num ); // num is not defined;

    在外面访问 num 肯定是访问不到,还会报错:num is not defined;
    但是你又想访问到 alwaysLinger 函数内部的局部变量。
    这是时候你就需要闭包了。

    function alwaysLinger(){
        var num = 0;
        // 一堆一堆逻辑
        return function(){
          return num;
        }
    }
    console.log(  alwaysLinger()() );  //  0;

    看起来,好像也没有什么神奇之处,当你调用alwaysLinger函数后,返回一个函数,执行返回的函数,再次返回 num 变量。

    这样就取了函数内部的值。看到这里你可能你会迷惑, 为什么要返回一个函数,然后再返回num呢,直接返回num不好吗?

    我建议你带着这个疑问看下去。


    闭包其实是一种现象:
    父函数内的子函数,被父函数之外的变量引用,父函数里的变量永远不会销毁(也说明了js的垃圾回收机制不会生效,父函数中的变量永远存在)

    下面举几个例子,简单的说明闭包常见的运用场景。

    1、使变量始终存在内存中

    function alwaysLinger(){
      var a = 0;
      return function(){
        return a++;
      }
    }
    
    console.log( alwaysLinger()() ); // 0
    console.log( alwaysLinger()() ); // 0
    console.log( alwaysLinger()() ); // 0
    console.log( alwaysLinger()() ); // 0
    
    var func = alwaysLinger();
    
    console.log( func() ); // 1
    console.log( func() ); // 2
    console.log( func() ); // 3
    console.log( func() ); // 4
    console.log( func() ); // 5

    前几个console,输出同一个值,是因为每次执行都返回一个新值,
    后几个console,输出递增的值,是因为每次执行后操作的都是一个值,就是函数中的局部变量num。我们使用闭包不单单只是为了取到局部变量,还要去操纵它。


    2、模拟块级作用域

    比如,我们现在要实现点击列表,实现弹出列表的索引值。

    // 先获取到列表项 li
    var list = document.querySelectorAll("li");
    // 遍历 li 
    for (var i = 0; i < list.length; i++) {
      // 给每一个列表项绑定点击事件,打印索引。
      list[i].onclick = function(){
        alert(i);
      }
    }

    一起看起来,好像并没有什么毛病,但是运行的结果却是不管点击那个都是返回最后一个索引值。这是因为在es5中没有块级作用域的原因导致的, 解决的办法很多种举几种常用的解决办法。

    1、闭包方式
      var list = document.querySelectorAll("li");
      for (var i = 0; i < list.length; i++) {
        (function(i){
          list[i].onclick = function(){
            console.log(i);
          }
        })(i) 
      }
    // 循环内部加一个自执行函数,利用函数作用域,模拟块级作用域。
    
    2、利用let代替var声明, 告诉浏览器使用es6语法
      var list = document.querySelectorAll("li");
        for (let i = 0; i < list.length; i++) {
          list[i].onclick = function(){
            console.log(i);
          } 
        }
    
    3、 使用forEach 循环
      var list = document.querySelectorAll("li");
      list.forEach(function(item,i){
        item.onclick = function(){
          console.log(i);
        }
      })
    
    2,3两种方法跟今天没有关系,这里就不多做讨论。

    来一个比较坑的面试题。

    function createFunction () {
      var result = new Array();
      for (var i = 0; i < 10; i++) {
        result[i] = function(){
          return i;
        }   
      }
      return result;
    }
    var aFunction = createFunction ();
    aFunction.forEach(function(obj){
      console.log( obj() );
    })

    大家可以自己尝试着猜猜,结果是什么?

    再来一个差不多的面试题。

    for (var i = 0; i < 10; i++) {
      setTimeout(function() {
        console.log(i);
      }, 0);
    }

    细心的小伙伴或许已经发现了,这种比较迷惑人的面试题,都有一个共同的特征,就是在循环中包含了一个函数。只要遇到这种问题,都可以用闭包来解决。


    3、代替全局变量

    我们都知道如果一个程序全局变量太多的话,会影响性能, 那么怎么解决呢,其实最根本的就是把全局变量变成局部变量;

    (function(){
      // 对于小范围使用的变量,都可以利用这种方法变成局部变量。
      var name = "HoChine";
      console.log(name);
    
      function test (){
        console.log(name);
      }
      test();
    
      /*..... 一堆堆逻辑 .....*/
    })()
    
    console.log(name) // name not defined;

    // 这样的话,在外界就访问不到变量了,避免了全局变量的增多。<p>---恢复内容结束---</p># 闭包

    以上就是我对闭包的理解,如果有错误的地方请提出,欢迎批评指正。一起学习一起进步。

  • 相关阅读:
    记CentOS-7-x86_64-DVD-1503与Windows7单硬盘双系统的安装
    NetCFSvcUtil.exe and Windows 7
    qq 通信原理(转)
    Qt通过odbc读取excel数据
    Qt中gb2312/GBK的URL编解码函数
    Qt将表格table保存为excel(odbc方式)
    Qt根据类名创建对象(元对象反射)
    亲试,Windows平台上使用Qt5.2.1编写Android
    使用正则表达式限制swing (JTextField等) 的输入
    Winform的"透明"
  • 原文地址:https://www.cnblogs.com/HoChine/p/6653121.html
Copyright © 2011-2022 走看看