zoukankan      html  css  js  c++  java
  • 闭包的总结(干货1)

    闭包:函数可以记住所在词法作用域,就产生了闭包,即使函数在当前词法作用域之外执行 ,闭包无处不在。。。请忽略这句话 ,看完在说

    function foo() {
            var a = 1;
            function bibao() {
                console.log(a);
            }
            return bibao;
        }
        var baz = foo();
        baz();

    bibao()能访问foo的内部作用域,然后bibao()本身作为值传递给baz,在foo执行之后,注意 baz=foo(),这里foo()会先运行,然后将返回值赋值给baz,然后运行baz(),实际是通过不同的标识符引用内部的函数bibao();bibao()可以正常的执行,实现了bibao()在自己定义时的词法作用域以外执行。foo执行完成之后,通常期待foo()整个内部空间被销毁,被垃圾回收器回收空间,但是,拜baz()所赐,baz能访问foo()作用域,使得该作用域一直纯在。

    这个例子中,bibao对作用域的引用就叫闭包

    再来看一个传递函数是间接的:

    var fn;
        function foo() {
            var a=2;
            function bibao() {
                console.log(a)
            }
            fn=bibao;
        }
        function bar() {
            fn(); 
        }
        foo();
        bar();

    同上面例子,不过这里外部函数运行,实现了全局变量fn对bibao()的引用,bibao()能访问foo内部词法作用域,在运行fn()的时候 ,就能实现了闭包

    再比如:

    function foo() {
    var a = 2;
    function baz() {
    console.log( a ); // 2
    }
    bar( baz );
    }
    function bar(fn) {
    fn(); // 闭包
    }

    运行foo()实现baz的引用绑定到bar作用域 ,bar是全局函数

    总结:

    无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用
    域的引用,无论在何处执行这个函数都会使用闭包。

    在比如我们经常写的函数:

    function wait(message) {
            setTimeout( function timer() {
                console.log( message );
            }, 1000 );
        }
        wait( "Hello, closure!" );

    这也是闭包

    将一个内部函数(名为timer)传递给setTimeout(..)。timer 具有涵盖wait(..) 作用域的闭包,因此还保有对变量message 的引用。
    wait(..) 执行1000 毫秒后,它的内部作用域并不会消失,timer 函数依然保有wait(..)作用域的闭包(也就是引用)。

    function setupBot(name, selector) {
    $( selector ).click( function activator() {
    console.log( "Activating: " + name );
    } );
    }
    setupBot( "Closure Bot 1", "#bot_1" );
    setupBot( "Closure Bot 2", "#bot_2" );

    同理:事件函数也是闭包,本质上只要用到回调函数的都是使用闭包


    在比如:

    for (var i=1; i<=5; i++) {
            setTimeout( function timer() {
                console.log( i );   //结果是输出5次6
            }, i*1000 );
         }   

    显而易见的,延迟函数的回调会在循环结束时才执行,为什么会捕捉不到?

    它们被封闭在一个共享的全局作用域中,因此实际上只有一个i

    解决办法:延迟函数的回调重复定义五次,完全不使用循环,用匿名函数立即执行产生5个数作用域包裹对应变量i

    for (var i=1; i<=5; i++) {
    (function() {
    setTimeout( function timer() {
    console.log( i );
    }, i*1000 );
    })();
    }

    当然,你也可以直接用ES新特性:

    for (let i=1; i<=5; i++) {
            setTimeout( function timer() {
                console.log( i );
            }, i*1000 );
        }

    延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问

    在比如:模块的例子,一个函数返回一个公用的对象接口

    function CoolModule() {
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething() {
    console.log( something );
    }
    function doAnother() {
    console.log( another.join( " ! " ) );
    }
    return {
    doSomething: doSomething,
    doAnother: doAnother
    };
    }
    var foo = CoolModule();
    foo.doSomething(); // cool
    foo.doAnother(); // 1 ! 2 ! 3

    同理,调用CoolModule();实现了内部函数引用传递给外部词法作用域 ,foo就能访问CoolModule内部

    1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块
    实例)。
    2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并
    且可以访问或者修改私有的状态。

    实现单例模式

    var foo = (function CoolModule() {
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething() {
    console.log( something );
    }
    function doAnother() {
    console.log( another.join( " ! " ) );
    }
    return {
    doSomething: doSomething,
    doAnother: doAnother
    };
    })();

    大量代码例子出自,原谅我剽窃,实在是让人豁然开朗

    《Javascript 权威指南》

    《你不知道的javascript》

  • 相关阅读:
    codevs 2021 中庸之道
    bzoj 1227: [SDOI2009]虔诚的墓主人
    cogs 2620. [HEOI2012]朋友圈
    bzoj 3123: [Sdoi2013]森林(45分暴力)
    cogs 1685 魔法森林
    bzoj 1061: [Noi2008]志愿者招募
    poj 1743 Musical Theme
    bzoj 1001: [BeiJing2006]狼抓兔子
    bzoj 4006: [JLOI2015]管道连接
    hdu 5693 D Game
  • 原文地址:https://www.cnblogs.com/godbutton/p/6155119.html
Copyright © 2011-2022 走看看