Skip to content

作用域闭包 #30

@paddingme

Description

@paddingme
  1. 看起来像闭包。

    function foo(){
        var a = 2;
        function bar(){
            console.log(a);
        }
        bar();
    }
    foo();
  2. 这才是闭包。

    function foo(){
        var a = 2;
        function bar(){
            console.log(a);
        }
        return bar;
    }
    var baz = foo();
    baz();
  3. 把内部函数 baz 传递给 bar, 当调用这个内部函数时(现在叫做 fn),它涵盖的 foo()
    内部作用域的闭包就可以观察到了。因此它能够访问 a。

    function foo() {
        var a = 2;
        function baz(){
            console.log(a);
        }
        bar(baz);
    }
    function bar(fn) {
        fn();
    }
  4. 把 baz 分配给全局变量

    var fn;
    function foo(){
        var a = 2;
        function baz(){
            console.log(a);
        }
        fn = baz;
    }
    function bar(){
        fn();
    }
    foo();
    bar();
  5. 无论通过何种手段将内部函数传递到所在的词法作用域以外,
    它都会持有对原始定义作用有的引用,
    无论在何处执行这个函数都会使用闭包。

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

    /*
    jquery
    */

    function setupBot(name,selector){
        $(selector).click(function activator(){
            console.log("Activating: " + name);
        });
    }
    setupBot("console BOT1","#bot_1");
    setupBot("console BOT2","#bot_2");
  6. IIFE

    var a = 2;
    (function IIFE(){
        console.log(a);
    })();
  7. 循环和闭包

    for(var i = 1; i <= 5; i++) {
        setTimeout(function timer(){
            console.log(i)
        },i*1000)
    }
    
    
    for(var i = 0; i<=5; i++) {
        (function(j){
            setInterval(function(){
                console.log(j);
            },1000);
        })(i);
    }
    // 每隔一秒钟打印1,2,3,4,5
    function foo(arr){
        var i = 0;
        return function(){
            setInterval(function(){
                console.log(arr[i++]);
            },1000);
        }()
    }
    (function(arr){
        var i=0,length=arr.length;
        (function a(){
            setTimeout(function(){
                console.log(arr[i++]);
                i<length&&a();
            },1000)
        }())
    }([1,2,3,4]))
  8. 模块

function coolModule() {
     var sth = "cool";
     var another = [1,2,3];
     function doSth() {
        console.log(sth);
     }
     function doAnother() {
        console.log(another.join("!"));
     }
     return {
        doSth:  doSth,
        doAnother : doAnother
     }
}
var foo = new coolModule();
foo.doSth();
foo.doAnother();
  1. 模块模式需要两个必要条件:
    1. 必须有外部的封闭函数,该函数必须至少
      被调用一次(每次调用都会创建一个新的模块实例)
    2. 封闭函数必须至少返回至少一个内部函数,
      这样内部函数才能在私有作用域中形成闭包,
      并且可以访问或者修改私有的状态。
  2. 模块模式另一个简单强大的变化用法是,命名将要作为公共API 返回的对象。
``` js
var foo = (function coolModule(id){
    function change(){
        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.change();
foo.identify();
```
  1. 现代的模块管理
``` js
var MyModules = (function Manger() {
    var modules = {};
    function define(name, deps, impl) {
        for (var i = 0; i < deps.length; i++) {
            deps[i] = modules[deps[i]];
        }
        modules[name] = impl.apply(impl, deps);
    }
    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("hippo"));
foo.awesome();
```

当函数可以记住并访问所在的词法作用域,即使函数时在当前词法作用域之外执行,这时就产生了闭包。

模块有两个主要特征:

  1. 为创建内部作用域而调用了一个包装函数;
  2. 包装函数的返回值必须至少包含一个队内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions