閉包

閉包

閉包是什么署惯?

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時,就產(chǎn)生了閉包镣隶,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行极谊,簡單上講诡右,就是在一個函數(shù)中內(nèi)部的函數(shù)。

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

那么bar函數(shù)就是一個閉包,它可以在作用域外被訪問蜕依。
各種閉包實例:

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!" );

本質(zhì)上無論何時何地样眠,如果將函數(shù)當(dāng)作第一級的值類型并到處傳遞友瘤,就會由閉包,在定時器檐束、事件監(jiān)聽器辫秧、Ajax請求、跨窗口通信被丧、Web Workers或者其他的異步(或者同步)任務(wù)中盟戏,只要使用了回調(diào)函數(shù),實際上就是閉包

模塊

模塊實例:

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中就是模塊甥桂。
最常見的實現(xiàn)模塊模式的方法通常被稱為模塊暴露柿究。
模塊模式所具備的兩個必要特點:

必須有外部的封閉函數(shù),該函數(shù)至少被調(diào)用一次(每次調(diào)用都會產(chǎn)生新的模塊實例)黄选。
封閉函數(shù)必須返回至少一個內(nèi)部函數(shù)蝇摸,這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)办陷。

模塊模式的另一個簡單但強(qiáng)大的用法是命名將要作為公共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

通過在模塊實例的內(nèi)部保留對公共API對象的內(nèi)部引用,可以從內(nèi)部對模塊實例進(jìn)行修改民镜,包括添加或刪除方法和屬性啡专,以及修改他們的值。
現(xiàn)代的簡單模塊機(jī)制:

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的函數(shù)來定義的殃恒,“foo”甚至接受“bar”的實例作為依賴參數(shù)植旧,并能相應(yīng)的使用它。
它們符合前面列出的模塊模式的兩個特點:調(diào)用包裝了函數(shù)定義的包裝函數(shù)离唐,并且將返回值作為該模塊的API病附。

ES6的模塊機(jī)制
ES6中為模塊增加了一級語法支持。在通過模塊系統(tǒng)進(jìn)行加載時亥鬓,ES6會將文件當(dāng)作獨(dú)立的模塊來處理完沪。每個模塊都可以導(dǎo)入其他模塊或特定的API成員,同時也可以導(dǎo)出自身的API成員。
重構(gòu)之前的模塊得三個文件

  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導(dǎo)入到當(dāng)前作用域中覆积,并綁定在一個變量上听皿。
module方法:將整個模塊的API導(dǎo)入并綁定到一個變量上
export方法:將當(dāng)前模塊的一個標(biāo)識符導(dǎo)出為公共API
模塊文件中的內(nèi)容會被當(dāng)作包含在作用域閉包中一樣來處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宽档,一起剝皮案震驚了整個濱河市尉姨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吗冤,老刑警劉巖又厉,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異椎瘟,居然都是意外死亡覆致,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門肺蔚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煌妈,“玉大人,你說我怎么就攤上這事宣羊¤邓校” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵仇冯,是天一觀的道長腮猖。 經(jīng)常有香客問我,道長赞枕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任坪创,我火速辦了婚禮炕婶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘莱预。我一直安慰自己柠掂,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布依沮。 她就那樣靜靜地躺著涯贞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪危喉。 梳的紋絲不亂的頭發(fā)上宋渔,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音辜限,去河邊找鬼皇拣。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的氧急。 我是一名探鬼主播颗胡,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吩坝!你這毒婦竟也來了毒姨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钉寝,失蹤者是張志新(化名)和其女友劉穎弧呐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘩蚪,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡送火,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年单山,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡畸写,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出望浩,到底是詐尸還是另有隱情伤锚,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布险胰,位于F島的核電站汹押,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏起便。R本人自食惡果不足惜棚贾,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望榆综。 院中可真熱鬧妙痹,春花似錦、人聲如沸鼻疮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽判沟。三九已至耿芹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挪哄,已是汗流浹背吧秕。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留迹炼,地道東北人寇甸。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拿霉。 傳聞我的和親對象是個殘疾皇子吟秩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容