JavaScript閉包

閉包

閉包是什麼?

當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域以外執行,簡單上講,就是在一個函數中內部的函數。javascript

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

那麼bar函數就是一個閉包,它能夠在做用域外被訪問。
各類閉包實例:java

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或者其餘的異步(或者同步)任務中,只要使用了回調函數,實際上就是閉包閉包

模塊

模塊實例:app

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返回的對象。ide

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。code

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
模塊文件中的內容會被看成包含在做用域閉包中同樣來處理。事件

相關文章
相關標籤/搜索