zoukankan      html  css  js  c++  java
  • JavaScript--我所理解的闭包

    关于闭包大家肯定不陌生, 刚知道闭包这个特性那会真的被折磨, 现在找个时间把这些都记下来, 希望能帮新人理解和记忆.

    -------------------------------------------------------------------------------------------

    闭包的格式有很多种, 但基本都是将一个嵌套函数中的内层函数返回到外层函数外面, 使外层函数体外也能访问和操作外层函数中"封住"的局部变量, 与此同时, 外层函数中的变量不会被销毁, 会"长久记忆".

    最常见的闭包:

    function outer(){
        var i = 100;              //保存于外层函数体内的局部变量
        return function(){     //用于返回内部函数
              console.log(i);   //内部函数体中对 "它" 自己的上下文中的变量进行了操作
        }
    }
        var inner = outer();
        inner();    

    闭包的形式知道了, 重点是怎么调用, 关于调用的方式有很多:

    第一种:

    function outer(){
        var i = 100;              
        return function(){
              i++;          
              console.log(i);   
        }
    }
        var inner1 = outer();
        var inner2 = outer();
        inner1();  //输出结果为 101;
        inner2();  //输出结果还是 101;

    这是为何呢? 原因是  当 外层函数每执行一次, 就会创建新的闭包, 即每次外层函数的执行都开辟新的内存空间, 虽然 inner1 和 inner2 都是函数的引用, 但每次返回的函数都是"新的", 所以 inner1 是 inner1, inner2 是 inner2.

    第二种

    function outer(){
        var i = 100;              
        return function(){
              i++;          
              console.log(i);   
        }
    }
        var inner1 = outer();
        var inner2 = inner1;
        inner1(); //输出 101
        inner2(); //输出 102

    这种调用方式和第一种相比就很好理解了, 因为 inner1 和 inner2 都是"同一个函数"的引用, 所以出现 101 和 102 也就通了.

    第三种

    function fo(){
        var i = 0;
        return function(n){
        return n + i++;
        }
    }
    
    var f = fo();      //现在变量f是一个函数的引用
    var a = f(15);     //输出 15, i此时的值是0, 不是undefind, 闭包能够记住自己认识变量i
    var b = fo()(15);  //输出 15, fo()会返回新的内层函数, 也就是说产生了新的闭包(对比第一种理解), i已经被清零
    var c = fo()(20);  //输出 20, 同理
    var d = f(20);    //输出 21

    第三种函数做了稍稍改变, 内层函数中要传入形式参数, 其实道理是一样的.

    第四种

    function fo(m){
        return function(n){
        return m + n;
        }
    }
    
    var f = fo(1);      //变量 f 是一个内层函数的引用, 此时 f 已"记住" f 自己中要调用的 m 的值为 1
    var a = f(2);       //输出 3, 因为闭包的存在, 所以在 f(2) 执行时, f 内部的 m 是 1
    var b = fo(10)(1);  //输出 11, 每当外层函数 fo 执行一次, 都会产生新的闭包并返回一个函数
    var c = fo(10)(2);  //输出 12。同理。
    var d = f(3);       // 和 f(2) 同理.

    第五种

    其实第五种来说, 不单单是闭包的影响, 还有就是关于"异步"的一些概念, 就是浏览器给元素添加监听是要消耗时间的, 所以在给一个类数组中的元素通过循环添加监听是不能直接添加的.下面是 html 结构.

        <ul>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>

    下面是对应的 JS 代码:

    
    

    var lis = document.getElementsByTagName("li");
    for(var i = 0, length = lis.length; i < length; i++){
      lis[i].onclick = function (){
        console.log("我被点击了, 我是第" + i);
      };
    }

    初学者可以试试, 会发现每个 li 触发点击后再控制台输出的都是 "我被点击了, 我是第 5", 原因是 当 浏览器给 元素添加监听时, 不会阻塞程序的执行, 就是说在给 lis[0] 添加事件监听时, for 循环还在走, i 还在 ++.

    解决方法最好用的就是 IIFE(即立即执行函数):

    var lis = document.getElementsByTagName("li");
    for(var i = 0, length = lis.length; i < length; i++){
        lis[i].onclick = (function(i){
                    return function (){
                        console.log("我被点击了, 我是第" + i);
                                    };
                })(i);
    }        

    外层函数是立即执行的, 并且把 i 当做参数传入函数体, 因此返回的内层函数就会拿到"合适的" i 值.

    以上是我对闭包的一些理解, 在此抛砖引玉, 有误导之处还请不吝指点 ^^.

  • 相关阅读:
    [CSP校内集训]2019.10.16数学专题
    Knights of the Round Table(缩点+判奇环) poj 2942 && 洛谷SP2878
    机房测试7:exam(二进制+模拟)
    机房测试9:gift(单调队列优化dp)
    机房测试9:hotel(神奇dp)
    机房测试8:question(求最大1矩阵:悬线法 or 二分)
    机房测试7:paint(分治+st表)
    机房测试6:矿石(优先队列)
    机房测试6:括号序列(hash+栈 )
    bzoj1123 && 洛谷 P3469 tarjan割点的运用
  • 原文地址:https://www.cnblogs.com/vlovecode/p/6031404.html
Copyright © 2011-2022 走看看