作用域與閉包(2)

1佃延、閉包
當函數(shù)可以記住并訪問所在的詞法作用域時,就產(chǎn)生了閉包脸甘,即使函數(shù)是在當前詞法作用域之外執(zhí)行。

function foo()
{
    var a = 2;
    function bar()
    {
        console.log(a);
    }
    return bar;
}
bar baz = foo();
baz(); //2,這就是閉包

基于詞法作用域的查找規(guī)則(RHS引用查詢)偏灿,函數(shù)bar()可以訪問外部foo()作用域中的變量a丹诀。然后我們將bar()函數(shù)本身當作一個值類型進行傳遞。在foo()執(zhí)行后翁垂,其返回值铆遭,實際上只是通過不同的標識符引用調(diào)用了內(nèi)部函數(shù)bar()。

因為bar()是在自己定義的詞法作用域以外的地方執(zhí)行沿猜,bar()擁有涵蓋foo()內(nèi)部作用域的閉包枚荣。所以foo()執(zhí)行后其內(nèi)部作用域不會被銷毀。

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

將一個內(nèi)部函數(shù)(timer)傳遞給setTimeout()啼肩。timer具有涵蓋wait()作用域的閉包橄妆,因此還保有對變量message的引用。深入到引擎的內(nèi)部原理中祈坠,內(nèi)置的工具函數(shù)setTimeout()持有對一個參數(shù)的引用害碾。引擎會調(diào)用這個函數(shù)(timer),而詞法作用域在這個過程中保持完整——閉包赦拘。

2慌随、循環(huán)和閉包

for (var i = 1; i <= 5; i++)
{
    setTimeout(function timer()
    {
        console.log(i);
    }, i * 1000);
}

我們期待這段代碼行為是分別輸出數(shù)字1~5,每秒一次另绩,每次一個儒陨。但實際上花嘶,輸出的是以每秒一次的頻率輸出五次6。我們試圖假設循環(huán)中的每個迭代在運行時會給自己捕獲一個i的副本蹦漠。但根據(jù)作用域的工作原理椭员,實際情況是盡管循環(huán)中的五個函數(shù)是在各個迭代中分別定義的,但是它們都被封閉在一個共享的全局作用域中笛园,因此只有一個i隘击。
用IIFE解決:

for (var i = 1; i <= 5; i++)
{
    (function(j)
    {
        setTimeout(function timer()
        {
            console.log(i);
        }, i * 1000);
    })(i);
}

用塊作用域解決:
使用IIFE在每次迭代時都創(chuàng)建一個新的作用域。也就說每次迭代需要一個塊作用域研铆。

    
for (let i = 1; i <= 5; i++)
{
    setTimeout(function timer()
    {
        console.log(i);
    }, i * 1000);
}

塊作用域本質(zhì)上是將一個塊轉換成一個可以被關閉的作用域埋同。

3、模塊

function CoolModule()
{
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething()
    {
        console.log(something);
    }
    function doAnother()
    {
        console.log(another.join("i"));
    }
    return
    {
        doSomething : doSomething,
        doAnother : doAnother
    };
}
var foo = CoolModule();
foo.doSomething(); //cool
foo.doAnother(); //1!2!3

首先棵红,CoolModule()只是一個函數(shù)凶赁,必須要通過它來創(chuàng)建一個模塊實例,其次逆甜,CoolModule()返回一個用對象字面量語法{key:value}來表示的對象虱肄。這個返回的對象中含有對內(nèi)部函數(shù)而不是內(nèi)部數(shù)據(jù)變量的引用。保持內(nèi)部數(shù)據(jù)變量是隱藏私有的狀態(tài)交煞∮搅可以將這個對象類型的返回值看作本質(zhì)上是模塊的公共API。
模塊模式需要具備兩個必要條件:

  1. 必須有外部的封閉函數(shù)素征,該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會創(chuàng)建一個新的模塊實例)
  2. 封閉函數(shù)必須返回至少一個內(nèi)部函數(shù)集嵌,這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)御毅。

現(xiàn)代的模塊機制

var MyModules = (function Manager(){
    var modules = {};
    function define(name, deps, impl){
        for (var i = 0; i < deps.length; i++){
            deps[i] = modules[deps[i]];
        }
        modules[name] = impl.apple(impl, deps);
    }
    function get(name){
        return modules[name];
    }
    return{
        define : define,
        get : get
    }
})();

這段代碼的核心是modulus[name] = impl.apply(impl,deps)根欧。為了模塊的定義引入了包裝函數(shù),并且將返回值存儲在一個根據(jù)名字來管理的模塊列表中亚享。

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")); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO

foo和bar模塊都是通過一個返回公共API的函數(shù)來定義的咽块。foo甚至接受bar的示例作為依賴參數(shù)绘面,并能相應地使用它欺税。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市揭璃,隨后出現(xiàn)的幾起案子晚凿,更是在濱河造成了極大的恐慌,老刑警劉巖瘦馍,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歼秽,死亡現(xiàn)場離奇詭異,居然都是意外死亡情组,警方通過查閱死者的電腦和手機燥筷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門箩祥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肆氓,你說我怎么就攤上這事袍祖。” “怎么了谢揪?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵蕉陋,是天一觀的道長。 經(jīng)常有香客問我拨扶,道長凳鬓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任患民,我火速辦了婚禮缩举,結果婚禮上,老公的妹妹穿的比我還像新娘匹颤。我一直安慰自己蚁孔,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布惋嚎。 她就那樣靜靜地躺著杠氢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪另伍。 梳的紋絲不亂的頭發(fā)上鼻百,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音摆尝,去河邊找鬼温艇。 笑死,一個胖子當著我的面吹牛堕汞,可吹牛的內(nèi)容都是我干的勺爱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼讯检,長吁一口氣:“原來是場噩夢啊……” “哼琐鲁!你這毒婦竟也來了?” 一聲冷哼從身側響起人灼,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤围段,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后投放,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奈泪,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了涝桅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拜姿。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冯遂,靈堂內(nèi)的尸體忽然破棺而出砾隅,到底是詐尸還是另有隱情,我是刑警寧澤债蜜,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布晴埂,位于F島的核電站,受9級特大地震影響寻定,放射性物質(zhì)發(fā)生泄漏儒洛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一狼速、第九天 我趴在偏房一處隱蔽的房頂上張望琅锻。 院中可真熱鬧,春花似錦向胡、人聲如沸恼蓬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽处硬。三九已至,卻和暖如春拇派,著一層夾襖步出監(jiān)牢的瞬間荷辕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工件豌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疮方,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓茧彤,卻偏偏與公主長得像骡显,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子曾掂,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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