闭包

作者: 饥人谷_御风 | 来源:发表于2017-12-13 22:52 被阅读0次

    闭包

    闭包是什么?

    当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行,简单上讲,就是在一个函数中内部的函数。

    function foo() {
    var a = 2;
    function bar() {
    console.log( a );
    }
    return bar;
    }
    var baz = foo();
    baz(); // 2 —— 朋友,这就是闭包的效果。
    

    那么bar函数就是一个闭包,它可以在作用域外被访问。
    各种闭包实例:

    function foo() {
    var a = 2;
    function baz() {
    console.log( a ); // 2
    }
    bar( baz );
    }
    function bar(fn) {
    fn(); // 妈妈快看呀,这就是闭包!
    }
    
    function setupBot(name, selector) {
    $( selector ).click( function activator() {
    console.log( "Activating: " + name );
    } );
    }
    setupBot( "Closure Bot 1", "#bot_1" );
    setupBot( "Closure Bot 2", "#bot_2" );
    
    function wait(message) {
    setTimeout( function timer() {
    console.log( message );
    }, 1000 );
    }
    wait( "Hello, closure!" );
    

    本质上无论何时何地,如果将函数当作第一级的值类型并到处传递,就会由闭包,在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是闭包

    模块

    模块实例:

    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
    

    这种模式在JavaScript中就是模块。
    最常见的实现模块模式的方法通常被称为模块暴露
    模块模式所具备的两个必要特点:

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

    模块模式的另一个简单但强大的用法是命名将要作为公共API返回的对象。

    var foo = (function CoolModule(id) {
    function change() {
    // 修改公共 API
    publicAPI.identify = identify2;
    }
    function identify1() {
    console.log( id );
    }
    function identify2() {
    console.log( id.toUpperCase() );
    }
    var publicAPI = {
    change: change,
    identify: identify1
    };
    return publicAPI;
    })( "foo module" );
    foo.identify(); // foo module
    foo.change();
    foo.identify(); // FOO MODULE
    

    通过在模块实例的内部保留对公共API对象的内部引用,可以从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改他们的值。
    现代的简单模块机制:

    var MyModules = (function Manager() {
                var modules = {}
    
                function define(name, argus, func) {
                    for (var i = 0; i < argus.length; i++) {
                        argus[i] = modules[argus[i]]
                    }
    
                    modules[name] = func.apply(func,argus)
                }
    
                function get(name) {
                    return modules[name]
                }
    
                return {
                    define: define,
                    get: get
                }
            })()
    
            MyModules.define("bar", [], function () {
                function hello(who) {
                    return "Let me introduce: " + who;
                }
                return {
                    hello: hello
                };
            });
            MyModules.define("foo", ["bar"], function (bar) {
                var hungry = "hippo";
                function awesome() {
                    console.log(bar.hello(hungry).toUpperCase());
                }
                return {
                    awesome: awesome
                };
            });
            var bar = MyModules.get("bar");
            var foo = MyModules.get("foo");
    
            console.log(
                bar.hello.call(undefined, "hippo")
            ); // Let me introduce: hippo
            foo.awesome.call(undefined); // LET ME INTRODUCE: HIPPO
    

    "foo"和"bar"模块都是通过一个返回公共API的函数来定义的,“foo”甚至接受“bar”的实例作为依赖参数,并能相应的使用它。
    它们符合前面列出的模块模式的两个特点:调用包装了函数定义的包装函数,并且将返回值作为该模块的API。

    ES6的模块机制
    ES6中为模块增加了一级语法支持。在通过模块系统进行加载时,ES6会将文件当作独立的模块来处理。每个模块都可以导入其他模块或特定的API成员,同时也可以导出自身的API成员。
    重构之前的模块得三个文件

    1. bar.js
             function hello(who) {
                 return "Let me introduce: " + who
             }
             export hello
    
    1. foo.js
             import hello from "bar"
             var hungry = "hippo"
             function awesome() {
                 console.log(hello(hungry).toUpperCase())
             }
    
             export awesome
    
    1. baz.js
             module foo from "foo"
             module bar from "bar"
             console.log(bar.hello("hippo"))
             foo.awesome
    

    import方法:将一个模块中的一个或多个API导入到当前作用域中,并绑定在一个变量上。
    module方法:将整个模块的API导入并绑定到一个变量上
    export方法:将当前模块的一个标识符导出为公共API
    模块文件中的内容会被当作包含在作用域闭包中一样来处理。

    相关文章

      网友评论

          本文标题:闭包

          本文链接:https://www.haomeiwen.com/subject/gcltwxtx.html