盒子
目录

作用域闭包

文章取自我的 Github repos: Learning-JavaScript, 作者:@paddingme
原文链接:https://github.com/paddingme/Learning-JavaScript/issues/30

  1. 看起来像闭包。

    1
    2
    3
    4
    5
    6
    7
    8
    function foo(){
    var a = 2;
    function bar(){
    console.log(a);
    }
    bar();
    }
    foo();
  2. 这才是闭包。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function foo() {
    var a = 2;
    function baz(){
    console.log(a);
    }
    bar(baz);
    }
    function bar(fn) {
    fn();
    }
  4. 把 baz 分配给全局变量

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

    1
    2
    3
    4
    5
    6
    function wait(message) {
    setTimeout(function timer(){
    console.log(message);
    },1000)
    }
    wait("Hello,Closure!")

    /
    jquery
    /

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
    6
    7
    8
    9
    // 每隔一秒钟打印1,2,3,4,5
    function foo(arr){
    var i = 0;
    return function(){
    setInterval(function(){
    console.log(arr[i++]);
    },1000);
    }()
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    (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. 模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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();
  9. 模块模式需要两个必要条件:

    1. 必须有外部的封闭函数,该函数必须至少
      被调用一次(每次调用都会创建一个新的模块实例)
    2. 封闭函数必须至少返回至少一个内部函数,
      这样内部函数才能在私有作用域中形成闭包,
      并且可以访问或者修改私有的状态。
  1. 模块模式另一个简单强大的变化用法是,命名将要作为公共API 返回的对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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();
  2. 现代的模块管理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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. 包装函数的返回值必须至少包含一个队内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。