閉包(closure)

閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合串慰。MDN

MDN上的栗子
function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();
  • JavaScript中的函數(shù)會形成閉包。 閉包是由函數(shù)以及創(chuàng)建該函數(shù)的詞法環(huán)境組合而成。這個(gè)環(huán)境包含了這個(gè)閉包創(chuàng)建時(shí)所能訪問的所有局部變量菜职。
  • myFunc 是執(zhí)行 makeFunc 時(shí)創(chuàng)建的 displayName 函數(shù)實(shí)例的引用,而 displayName 實(shí)例仍可訪問其詞法作用域中的變量旗闽,即可以訪問到 name 酬核。由此,當(dāng) myFunc 被調(diào)用時(shí)适室,name 仍可被訪問嫡意,其值 Mozilla 就被傳遞到alert中。

為什么要使用閉包捣辆?

局部變量無法共享和長久的保存蔬螟,而全局變量可能造成變量污染,所以我們希望有一種機(jī)制既可以長久的保存變量又不會造成全局污染汽畴。

怎么樣使用閉包旧巾?

  • 定義外層的函數(shù)(outer),封裝被保護(hù)的局部變量忍些;
  • 定義內(nèi)層的函數(shù)(inner)鲁猩,執(zhí)行對外層函數(shù)變量的引用;
  • 外層函數(shù)(outer)返回內(nèi)層(inner)的函數(shù)對象罢坝,并且外層函數(shù)(outer)被調(diào)用廓握,結(jié)果保存在一個(gè)全局的變量中。
注意一:手動解除引用
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

// 釋放對閉包的引用
add5 = null;
add10 = null;

add5 和 add10 都是閉包嘁酿。它們共享相同的函數(shù)定義疾棵,但是保存了不同的環(huán)境。在 add5 的環(huán)境中痹仙,x 為 5是尔。而在 add10 中,x 則為 10开仰。最后通過 null 釋放了 add5 和 add10 對閉包的引用拟枚。

在javascript中薪铜,如果一個(gè)對象不再被引用,那么這個(gè)對象就會被垃圾回收機(jī)制回收恩溅;而makeAdder對象被全局變量add5和add10引用隔箍,就會占用內(nèi)存空間。

注意二:閉包遇上for循環(huán)

閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值脚乡,這是因?yàn)殚]包所保存的是整個(gè)變量對象蜒滩,而不是某個(gè)特殊的變量。

function test() {
  var arr = [];
  for(var i = 0;i < 10;i++) {
    arr[i] = function() {
      return i;
    };
  }
  for(var a = 0;a < 10;a++) {
    console.log(arr[a]());
  }
}
test();  // 連續(xù)打印10個(gè)10

討論區(qū)鏈接

函數(shù)1作用域
for(var i = 0; i < 10; i++) { 函數(shù)1作用域
        我在函數(shù)1作用域中
        arr[i] = function() { 函數(shù)2作用域
          我在函數(shù)2作用域中
          return i;
        };
}
函數(shù)1作用域
console.log(i);

console.log(i); 執(zhí)行到這里的時(shí)候奶稠,i 是10俯艰,既然這里是10,那么在函數(shù)2作用域中訪問i也是10锌订,因?yàn)楹瘮?shù)2作用域中沒有竹握,向上去函數(shù)1作用域中找,同一作用域中同一變量名的變量值肯定是相同的(未修改的情況下)辆飘。

當(dāng)你用 let 的時(shí)候啦辐,如下:

塊1作用域
for(let i = 0; i < 10; i++) { 塊2作用域
    我在塊2作用域中
    console.log(i); // 毫無疑問,這里的i從0依次增加到10  
    arr[i] = function() { 塊3作用域
      我在塊3作用域中
      return i;
    };
}
塊1作用域

當(dāng)你換成 let 的時(shí)候蜈项,讀取i的時(shí)候芹关,在當(dāng)前作用域塊3中沒有找到,向上一個(gè)作用域塊2尋找紧卒,在塊2作用域中發(fā)現(xiàn)i侥衬,于是拿到值。

閉包的缺點(diǎn)

  • 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中常侦,內(nèi)存消耗很大浇冰,所以不能濫用閉包贬媒,否則會造成網(wǎng)頁的性能問題聋亡,在IE中可能導(dǎo)致內(nèi)存泄露。
  • 閉包會在父函數(shù)外部际乘,改變父函數(shù)內(nèi)部變量的值坡倔。所以,如果你把父函數(shù)當(dāng)作對象(object)使用脖含,把閉包當(dāng)作它的公用方法(Public Method)罪塔,把內(nèi)部變量當(dāng)作它的私有屬性(private value)。
function Animal() {
  
  // 私有變量
  var series = "哺乳動物";
  function run() {
    console.log("Run!!!");
  }
  
  // 特權(quán)方法
  this.getSeries = function() {
    return series;
  };
}

思考題

function fun(n,o){
  console.log(o);
  return {
    fun: function(m){
      return fun(m,n);
    }
  };
}

var a = fun(0);  // ?
a.fun(1);        // ?        
a.fun(2);        // ?
a.fun(3);        // ?

var b = fun(0).fun(1).fun(2).fun(3);  // ?

var c = fun(0).fun(1);  // ?
c.fun(2);        // ?
c.fun(3);        // ?

參考文章

https://zhuanlan.zhihu.com/p/27857268
https://juejin.im/entry/57d60f7067f3560057e37e25
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末养葵,一起剝皮案震驚了整個(gè)濱河市征堪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌关拒,老刑警劉巖佃蚜,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庸娱,死亡現(xiàn)場離奇詭異,居然都是意外死亡谐算,警方通過查閱死者的電腦和手機(jī)熟尉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洲脂,“玉大人斤儿,你說我怎么就攤上這事】纸酰” “怎么了往果?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長踩蔚。 經(jīng)常有香客問我棚放,道長,這世上最難降的妖魔是什么馅闽? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任飘蚯,我火速辦了婚禮,結(jié)果婚禮上福也,老公的妹妹穿的比我還像新娘局骤。我一直安慰自己,他們只是感情好暴凑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布峦甩。 她就那樣靜靜地躺著,像睡著了一般现喳。 火紅的嫁衣襯著肌膚如雪凯傲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天嗦篱,我揣著相機(jī)與錄音冰单,去河邊找鬼。 笑死灸促,一個(gè)胖子當(dāng)著我的面吹牛诫欠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浴栽,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荒叼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了典鸡?” 一聲冷哼從身側(cè)響起被廓,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萝玷,沒想到半個(gè)月后嫁乘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體英遭,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡双肤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年妄田,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婆硬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稠项。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡创淡,死狀恐怖粥谬,靈堂內(nèi)的尸體忽然破棺而出宙地,到底是詐尸還是另有隱情送漠,我是刑警寧澤搂蜓,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布狼荞,位于F島的核電站,受9級特大地震影響帮碰,放射性物質(zhì)發(fā)生泄漏相味。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一殉挽、第九天 我趴在偏房一處隱蔽的房頂上張望丰涉。 院中可真熱鬧,春花似錦斯碌、人聲如沸一死。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽投慈。三九已至,卻和暖如春冠骄,著一層夾襖步出監(jiān)牢的瞬間伪煤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工凛辣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抱既,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓蟀给,卻偏偏與公主長得像蝙砌,于是被迫代替她去往敵國和親阳堕。 傳聞我的和親對象是個(gè)殘疾皇子跋理,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354