javaScript閉包

轉(zhuǎn)自知乎Rachel

什么是閉包

關(guān)于為什么在JavaScript中閉包得應(yīng)用都有關(guān)鍵詞"return",引用JavaScript秘密花園中的一段話:

閉包是JavaScript一個(gè)非常重要得特性辽剧,這意味著當(dāng)前作用域總是能夠訪問外部作用域中的變量霞怀。因?yàn)?函數(shù) 是JavaScript中唯一擁有自身作用域的結(jié)構(gòu),因此閉包的創(chuàng)建依賴于函數(shù)

閉包是一個(gè)內(nèi)部函數(shù),可以訪問外部(封閉)函數(shù)的變量范圍鏈劫樟。閉包有三個(gè)范圍鏈:它可以訪問自己的范圍(在大括號(hào)之間定義的變量),它可以訪問外部函數(shù)的變量织堂,并且它可以訪問全局變量

內(nèi)部函數(shù)不僅可以訪問外部函數(shù)的變量叠艳,還可以訪問外部函數(shù)的參數(shù),需要注意的是:即使可以直接調(diào)用外部函數(shù)的參數(shù)易阳,內(nèi)部函數(shù)仍然不能調(diào)用外部函數(shù)的參數(shù)對(duì)象附较。

通過在另一個(gè)函數(shù)中添加一個(gè)函數(shù)來創(chuàng)建一個(gè)閉包。

javaScript 閉包的基本示例:

function showName(firstName,lastName){
    var nameIntro = "Your name is " 
    // 這個(gè)內(nèi)部函數(shù)可以訪問外部函數(shù)的變量潦俺,包括參數(shù)
    function makeFullName(){
        return nameIntro + firstName + " " + lastName
    }

    return makeFullName()
}

showName("Michael","Jackson")

閉包在 Node.js 中廣泛使用拒课;它們是 Node.js 的異步,非阻塞構(gòu)架中的主軸事示。閉包也經(jīng)常用在 jQuery 和 javaScript 代碼中早像。

閉包的經(jīng)典 jQuery 示例:

$(function(){
    var selections = []
    $(".niners").click(function(){
        selections.push(this.prop("name"))
    })
})

閉包規(guī)則和副作用

  1. 閉包可以訪問外部函數(shù)的變量:
    閉包最顯著的特征就是閉包可以訪問到外部函數(shù)變量,即使外部函數(shù)變量已經(jīng)被返回很魂。
    JavaScript 中的函數(shù)執(zhí)行都是相同的作用域鏈(函數(shù)可以從內(nèi)往外訪問)扎酷。因此,您可以稍后在程序中調(diào)用內(nèi)部函數(shù)遏匆。此示例演示:
function celebrityName(firstName) {
    var nameIntro = "This celebrity is"
    // 這個(gè)內(nèi)部函數(shù)可以訪問外部函數(shù)的變量法挨,包括參數(shù)
    function lastName(theLastName) {
        return nameIntro + firstName + " " + theLastName
    }
    return lastName
}
var mjName = celebrityName('Michael')
//此時(shí)celebrityName外部函數(shù)返回
//閉包(lastName)在外部函數(shù)返回后調(diào)用
//然而,閉包仍然可以訪問到外部函數(shù)的變量和參數(shù)mjName("Jackson")

2.閉包引用外部函數(shù)的變量存儲(chǔ)幅聘,不存儲(chǔ)實(shí)際值凡纳。當(dāng)外部函數(shù)的變量的值在調(diào)用閉包之前改變時(shí),閉包變得更有趣帝蒿。這個(gè)強(qiáng)大的功能可以創(chuàng)造性的利用荐糜,看如下代碼:

function celebrityID() {
    var celebrityID = 999;
    //用內(nèi)部函數(shù)返回一個(gè)對(duì)象 
    //所有的內(nèi)部函數(shù)可以訪問外部函數(shù)的變量 
    return {
        getID: function () {
            //這個(gè)內(nèi)部函數(shù)將返回更新celebrityID變量 
            return celebrityID;
            //它將返回celebrityID的當(dāng)前值,即使changeTheID函數(shù)更改 
        },
        setID: function (theNewID) {
            //這個(gè)內(nèi)部函數(shù)會(huì)隨時(shí)改變外部函數(shù)的變量 
            celebrityID = theNewID;
        }
    }
}
var mjID = celebrityID();
// 此時(shí)葛超,該celebrityID外部函數(shù)返回暴氏。 
mjID.getID();
// 999? mjID.setID(567); 
// 改變外部函數(shù)變量 
mjID.getID(); // 567

3.閉包緩存
由于閉包可以訪問外部函數(shù)的變量的更新值,當(dāng)用一個(gè)for循環(huán)改變外部函數(shù)的變量時(shí)绣张,也可能導(dǎo)致錯(cuò)誤答渔。從而:

function celebrityIDCreator(theCelebrities) { 
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) { 
        theCelebrities[i]["id"] = function () { 
            return uniqueID + i; 
        } 
    } 
    return theCelebrities; 
}
var actionCelebs = [
    { name: "Stallone", id: 0 }, 
    { name: "Cruise", id: 0 }, 
    { name: "Willis", id: 0 }
]; 
var createIdForActionCelebs = celebrityIDCreator(actionCelebs);
var stalloneID = createIdForActionCelebs[0]; 
console.log(stalloneID.id()); // 103

在前面的例子中,在調(diào)用匿名函數(shù)時(shí)侥涵,i的值未3(數(shù)組的長(zhǎng)度沼撕,然后遞增)宋雏。數(shù)字3已添加到uniqueID以為所有名人ID創(chuàng)建103.因此返回的數(shù)組中的每個(gè)位置都得到id = 103,而不是預(yù)期的100务豺,101磨总,102

發(fā)生這種情況的原因是,正如我們之前例子中討論的笼沥,閉包(本例中的匿名函數(shù))可以通過參數(shù)訪問外部函數(shù)的變量蚪燕,而不是一個(gè)值。因此敬拓,正如前面的例子所示邻薯,我們可以使用閉包訪問更新的變量,這個(gè)例子類似地訪問i變量改變時(shí)乘凸,因?yàn)橥獠亢瘮?shù)運(yùn)行整個(gè)for循環(huán)并返回i的最后一個(gè)值厕诡,即103

要解決在閉包這個(gè)副作用(錯(cuò)誤),你可以使用一個(gè)立即調(diào)用函數(shù)表達(dá)式:

function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
        theCelebrities[i]["id"] = function (j)  { //參數(shù)j是i通過這個(gè)IIFE調(diào)用?
            return function () {
                return uniqueID + j; 
                //for循環(huán)的每個(gè)迭代通過i的當(dāng)前值到這個(gè)IIFE并且保存當(dāng)前值到數(shù)組?
            } ()  //通過這個(gè)函數(shù)的末尾的()营勤,我們將立即執(zhí)行灵嫌,
                    并返回UNIQUEID + j的值,而不是返回一個(gè)函數(shù)葛作。?
        } (i); //立即調(diào)用函數(shù)傳遞變量i作為參數(shù)?
    }
?    return theCelebrities;
}
?
?var actionCelebs = [ {name:"Stallone", id:0}, {name:"Cruise", id:0}, 
                     {name:"Willis", id:0} ];
?
?var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
?
?var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100?
?
?var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id);// 101

或者使用ES6的內(nèi)容定義循環(huán)中的i

function celebrityIDCreator(theCelebrities) { 
    var uniqueID = 100;
    for (let i = 0; i < theCelebrities.length; i++) { 
        theCelebrities[i]["id"] = function () { 
            return uniqueID + i; 
        } 
    } 
    return theCelebrities; 
}
var actionCelebs = [
    { name: "Stallone", id: 0 }, 
    { name: "Cruise", id: 0 }, 
    { name: "Willis", id: 0 }
]; 
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
?
?var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100?
?
?var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id);// 101
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寿羞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赂蠢,更是在濱河造成了極大的恐慌绪穆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虱岂,死亡現(xiàn)場(chǎng)離奇詭異玖院,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)第岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門难菌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蔑滓,你說我怎么就攤上這事郊酒。” “怎么了键袱?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵燎窘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蹄咖,道長(zhǎng)荠耽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任比藻,我火速辦了婚禮铝量,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘银亲。我一直安慰自己慢叨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布务蝠。 她就那樣靜靜地躺著拍谐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馏段。 梳的紋絲不亂的頭發(fā)上轩拨,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音院喜,去河邊找鬼亡蓉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛喷舀,可吹牛的內(nèi)容都是我干的砍濒。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼硫麻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼爸邢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拿愧,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤杠河,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后浇辜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體券敌,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年奢赂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陪白。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膳灶,死狀恐怖咱士,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轧钓,我是刑警寧澤序厉,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站毕箍,受9級(jí)特大地震影響弛房,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜而柑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一文捶、第九天 我趴在偏房一處隱蔽的房頂上張望荷逞。 院中可真熱鬧,春花似錦粹排、人聲如沸种远。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坠敷。三九已至,卻和暖如春射富,著一層夾襖步出監(jiān)牢的瞬間膝迎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工胰耗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留限次,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓宪郊,卻偏偏與公主長(zhǎng)得像掂恕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弛槐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

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