我在閉包在node.js里的應(yīng)用與思考

閉包的定義

學(xué)術(shù)來說,閉包是指在 JavaScript 中捐凭,內(nèi)部函數(shù)總是可以訪問其所在的外部函數(shù)中聲明的參數(shù)和變量,即使在其外部函數(shù)被返回(壽命終結(jié))了之后。

就我個人而言的理解御板,從形式來看,閉包就是在函數(shù)里面定義一個函數(shù)牛郑,從特點來說怠肋,子函數(shù)能夠讀寫父函數(shù)的局部變量

function parent() {
   var count = 0;
   return function children(){
      count++;
      console.log(count);
   }
}

var children = parent();
children();  // 1
children();  // 2

如何辨別閉包

雖然生產(chǎn)中淹朋,我們一直極力回避復(fù)雜的閉包笙各,閉包容易降低代碼的閱讀性,但是大家卻非常喜歡考閉包础芍,知乎也有相關(guān)討論:
https://www.zhihu.com/question/30861304
就讓我們來看看那些面試中的閉包杈抢。

案例1:

function f1() {
  var n = 999;
  nAdd = function () { n += 1;}
  function f2() {
    console.log(n);
  }
}

var result = f1();
result(); // 999
nAdd(); //
result(); // 1000
  • 執(zhí)行f1返回得是一個f2的function。
  • 執(zhí)行f2 打印999仑性,其中n是f1的局部變量惶楼,作為閉包的f2能訪問父函數(shù)的局部變量。
  • 執(zhí)行nAdd方法,實際上歼捐,nAdd是一個匿名函數(shù)何陆,n作為引用傳入到匿名函數(shù)中,將匿名函數(shù)傳給nAdd豹储,因為nAdd方法沒有做var聲明贷盲,nAdd是一個全局函數(shù),執(zhí)行全局函數(shù)剥扣。調(diào)用nAdd的時候?qū)蛴?000

案例2

var tasks = [];

for (var i=0; i<3; i++) {
    tasks.push(function() {
        console.log('>>> ' + i); 
    });
}

相信大多數(shù)人都會回答晃洒,打印陸續(xù)打印1,2朦乏,3球及,而實際情況打印的是3,3呻疹,3 因為匿名函數(shù)保存的是i的引用吃引,當(dāng)for循環(huán)結(jié)束的時候,i已經(jīng)變成3了刽锤,所以打印的時候變成3镊尺,相對其他題目的考察方向,這個例子的考察的方向是如何利用閉包解決問題

for (var i=0; i<3; i++) {
    (function(n) {
        tasks.push(function() {
            console.log('>>> ' + n);
        });
    })(i);
}

閉包里的匿名函數(shù)并思,讀取變量的順序是庐氮,先讀取本地變量,再讀取父函數(shù)的局部變量宋彼,如果找不到到全局里面搜索弄砍,i作為局部變量存到閉包里面,所以調(diào)整后的代碼可以能正常打印输涕。

案例3:

是不是覺得有點感覺了音婶,看看以下案例?

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){//[2]
            return fun(m,n);//[1]
        }
    }
}

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

鑒于分析篇幅過大莱坎,就不做分析了衣式,可以參考:
http://segmentfault.com/a/1190000004187681

閉包與內(nèi)存泄漏

javascript的主要通過計數(shù)器的方式方式回收內(nèi)存,假設(shè)有A,B,C三個對象檐什,當(dāng)a引用b的時候碴卧,那么b的引用計數(shù)器增加1,同時b引用c的時候乃正,c計數(shù)器增加1住册,當(dāng)a被釋放的時候,b的計數(shù)器減少1烫葬,變成0被釋放界弧,c計數(shù)器變成0凡蜻,被釋放,然而垢箕,當(dāng)遇到b和c之間存在相互引用的時候划栓,就無法通過計數(shù)器方式釋放內(nèi)存,而必包恰好是導(dǎo)致這種情況發(fā)生的溫床条获。閉包不代表一定會帶來內(nèi)存泄漏忠荞,良好的閉包設(shè)計是正常內(nèi)存使用。

function parent() {
    var childrenVar = {a: 1}; 
    return function() { 
        console.log(childrenVar); 
    }   
}

var children = parent();
children(); //  {a: 1}

當(dāng)parent函數(shù)結(jié)束時發(fā)現(xiàn)childrenVar 變量被匿名函數(shù)占用帅掘,所以parent無法釋放委煤,導(dǎo)致內(nèi)存泄漏。

翻閱了不少資料修档,有人已經(jīng)對js的閉包做過不少測試碧绞,具體可以參考
http://justjavac.iteye.com/blog/1465169

閉包的應(yīng)用

說了那么多閉包的壞處,難道閉包就一無是處么吱窝?實際上讥邻,只要保證使用閉包的適合,不要重復(fù)創(chuàng)建院峡,不斷創(chuàng)建兴使,無休止創(chuàng)建,使用閉包并沒有想象中那么嚴重照激,例如发魄,當(dāng)我們需要利用閉包來實現(xiàn)統(tǒng)計,那是內(nèi)存使用俩垃,不叫內(nèi)存泄漏励幼。

這里列舉兩個例子

案例1

var middleware = function (s) {
    return function (req, res, next) {
        console.log(s);  // hello world
        return next();
    }
}

app.use(middleware('hello world'));

使用閉包能夠為 express中間件,傳遞參數(shù)到中間件當(dāng)中吆寨。

案例2

var request = require('request');
var _ = require('underscore');

var middleware = function (req, res, next) {
    req.request = function (options, callback) {
        _.extend(options, {headers: req.headers});
        request(options, callback);
    }
}

app.use(middleware);

app.get(function (req, res, next) {
    req.request('/api', function () {
       res.send('ok');
    })
})

現(xiàn)在有這么一個業(yè)務(wù)需求赏淌,做一個api代理,代理需要把原來api請求的header也帶上啄清,如果直接使用request模塊做轉(zhuǎn)發(fā),時間久了俺孙,很容易會導(dǎo)致忘記帶上原來請求的header辣卒,所以寫了一個request方法綁定到req上。

最后

閉包睛榄,并不是什么壞東西荣茫,不要每次提到閉包都與內(nèi)存泄漏掛鉤,正常使用閉包场靴,是使用內(nèi)存啡莉,不是內(nèi)存泄漏港准,請充分利用好閉包的特性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咧欣,一起剝皮案震驚了整個濱河市浅缸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魄咕,老刑警劉巖衩椒,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哮兰,居然都是意外死亡毛萌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門喝滞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阁将,“玉大人,你說我怎么就攤上這事右遭∽鲋眩” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵狸演,是天一觀的道長言蛇。 經(jīng)常有香客問我,道長宵距,這世上最難降的妖魔是什么腊尚? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮满哪,結(jié)果婚禮上婿斥,老公的妹妹穿的比我還像新娘。我一直安慰自己哨鸭,他們只是感情好民宿,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著像鸡,像睡著了一般活鹰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上只估,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天志群,我揣著相機與錄音,去河邊找鬼蛔钙。 笑死锌云,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吁脱。 我是一名探鬼主播桑涎,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼彬向,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了攻冷?” 一聲冷哼從身側(cè)響起娃胆,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎讲衫,沒想到半個月后缕棵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡涉兽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年招驴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枷畏。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡别厘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拥诡,到底是詐尸還是另有隱情触趴,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布渴肉,位于F島的核電站冗懦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仇祭。R本人自食惡果不足惜披蕉,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乌奇。 院中可真熱鬧没讲,春花似錦、人聲如沸礁苗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽试伙。三九已至嘁信,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疏叨,已是汗流浹背吱抚。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留考廉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓携御,卻偏偏與公主長得像昌粤,于是被迫代替她去往敵國和親既绕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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