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》

  • 相关阅读:
    第八章 多线程编程
    Linked List Cycle II
    Swap Nodes in Pairs
    Container With Most Water
    Best Time to Buy and Sell Stock III
    Best Time to Buy and Sell Stock II
    Linked List Cycle
    4Sum
    3Sum
    Integer to Roman
  • 原文地址:https://www.cnblogs.com/godbutton/p/6155119.html
Copyright © 2011-2022 走看看