JavaScript之閉包與高階函數(shù)(一)

點(diǎn)擊此處訪問我的github了解更多詳情

JavaScript雖是一門面向?qū)ο蟮木幊陶Z言棚瘟,但同時(shí)也有許多函數(shù)式編程的特性现斋,如Lambda表達(dá)式,閉包偎蘸,高階函數(shù)等庄蹋。

函數(shù)式編程是種編程范式,它將電腦運(yùn)算視為函數(shù)的計(jì)算迷雪。函數(shù)編程語言最重要的基礎(chǔ)是 λ 演算(lambda calculus)限书。而且λ演算的函數(shù)可以接受函數(shù)當(dāng)作輸入(參數(shù))和輸出(返回值

閉包

何謂閉包?對(duì)于閉包眾位各有己見章咧,今我試說之倦西,閉包,常指有權(quán)訪問其外部作用域中變量和參數(shù)的函數(shù)赁严。最常見的就是在某函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)扰柠。如:


    var count = (function() {
        var item = 0;
        
        return {
            add: function(num) {
                item += typeof num === 'number' ? num : 1;
            },
            value: function() {
                return item;
            }
        }
    })();

此處把函數(shù)返回的結(jié)果賦值給count,該函數(shù)返回一個(gè)包含兩個(gè)方法的對(duì)象疼约,對(duì)象中的方法均可訪問其包含函數(shù)中的變量及參數(shù)卤档。count中保存的是該對(duì)象的一個(gè)引用,對(duì)象中的方法依然可以訪問自執(zhí)行函數(shù)中的變量忆谓,而且訪問的是變量本身裆装。

閉包 函數(shù)可以訪問它創(chuàng)建時(shí)所處的上下文環(huán)境中的變量以及參數(shù),this以及arguments除外。

閉包其實(shí)并不是很好闡述,與我而言摊腋,自我理解與向他人闡述差別甚大,但也要試著去征服它琢唾。閉包的形成與變量息息相關(guān),尤其是變量的作用以及變量生命周期盾饮,請(qǐng)看細(xì)說:

閉包與變量

閉包中所保存的是整個(gè)變量對(duì)象--執(zhí)行環(huán)境(上下文環(huán)境)中的一個(gè)表示變量的對(duì)象的引用采桃,訪問執(zhí)行環(huán)境中變量即是訪問該變量對(duì)象中的變量懒熙。

變量對(duì)象 每個(gè)執(zhí)行環(huán)境(上下文環(huán)境)中的一個(gè)表示所有變量的對(duì)象,全局環(huán)境的變量對(duì)象始終存在普办,而局部環(huán)境的變量對(duì)象只在其執(zhí)行過程中存在工扎。

典型案例如下:


    function myNumber() {
        var count = [];
        for (var i = 0; i < 10; i ++) {
            count[i] = function() {
                return i;
            }
        }
        return count;
    }

這個(gè)函數(shù)會(huì)返回一個(gè)函數(shù)數(shù)組,這個(gè)數(shù)組會(huì)不會(huì)乖乖返回自己的數(shù)字呢衔蹲?當(dāng)然不會(huì)肢娘,事實(shí)上,每個(gè)函數(shù)都返回10舆驶。為什么呢橱健?細(xì)細(xì)道來,因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著myNumber()函數(shù)的活動(dòng)對(duì)象(變量對(duì)象)沙廉,他們都引用同一個(gè)變量對(duì)象拘荡,當(dāng)然也引用同一個(gè)變量i,當(dāng)myNumber()函數(shù)返回后i為10。

再看如下代碼:


    function myNumber() {
        var count = [];
        for (var i = 0; i < 10; i ++) {
            count[i] = (function(num) {
                return function() {
                    return num;
                };
            })(i);
        }
        return count;
    }

在此將自執(zhí)行匿名函數(shù)結(jié)果賦值給數(shù)組撬陵,調(diào)用每個(gè)匿名函數(shù)時(shí)珊皿,傳入變量i,而函數(shù)參數(shù)是按值傳遞袱结,即將變量值復(fù)制給參數(shù)num亮隙,在此匿名函數(shù)內(nèi)部又創(chuàng)建并返回了一個(gè)訪問num參數(shù)的閉包,count數(shù)組中的函數(shù)均保存有自己的num變量的副本垢夹,于是,便返回各自的值了维费。

變量的作用域

變量分全局變量與局部變量果元,在函數(shù)中聲明變量時(shí),以var關(guān)鍵字定義的變量即是局部變量犀盟,而不帶var關(guān)鍵字的就變成全局變量而晒。

    
    var c = 3
    var func = function() {
        var a = 1;
        b = 2;
        alert(b);//2
        alert(c);//3
    }
    func();
    alert(b);//2
    alert(a);//Uncaught ReferenceError: b is not defined

我們知道,在函數(shù)中查找變量時(shí)阅畴,首先在當(dāng)前函數(shù)執(zhí)行環(huán)境作用域查找倡怎,若未找到,則隨當(dāng)前執(zhí)行環(huán)境創(chuàng)建的作用域鏈往外層查找贱枣,直到全局對(duì)象為止监署,這里的查找是從內(nèi)向外查找的

變量的生命周期

上面說到變量作用域,這里談?wù)勛兞可芷冢?/p>

  • 全局變量纽哥,其生命周期在整個(gè)程序運(yùn)行時(shí)間內(nèi)永久存在钠乏,除非主動(dòng)銷毀,否則可以隨時(shí)調(diào)用春塌。
  • 局部變量晓避, 其在所屬作用域代碼執(zhí)行過程中存在簇捍,當(dāng)運(yùn)行完成,且不存在外部調(diào)用此上下文環(huán)境中的變量時(shí)俏拱,即被銷毀暑塑,否則依然存在。

    var func = function() {
        var res = [1,2,3,4,5,6];
        var a = 0;
        return function() {
            alert(res[a]);
            a++;
        }
    };
    var f = func();
    
    func()();//1
    func()();//1
    
    f();//1
    f();//2
    f();//3

試比較執(zhí)行fun()()與f()的彈出值锅必,是不一樣的事格,貌似在f()中a一直存在。當(dāng)執(zhí)行var f = func();時(shí)况毅,f函數(shù)返回的是一個(gè)匿名函數(shù)的引用分蓖,此匿名函數(shù)可以訪問func()被調(diào)用時(shí)的上下文環(huán)境(執(zhí)行環(huán)境),局部變量即在其中尔许,局部變量所處環(huán)境能被外界訪問么鹤,局部變量就不會(huì)被銷毀。

閉包的作用

  1. 封裝變量

閉包可以封裝形成‘私有變量‘味廊,如:實(shí)現(xiàn)計(jì)算乘積:


    var mult = function() {
        var a = 1;
        for (var i = 0, len = arguments.length; i < len; i++) {
            a = a * arguments[i];
        }
        return a;
    }
    alert(mult(1,2,3,4));
  1. 模仿塊級(jí)作用域

JavaScript中是沒有塊級(jí)作用域的概念的蒸甜,如:


    function block() {
        var res = [1,3,5,7,9];
        for (var i = 0; i < res.length; i++) {
            alert(res[i]);
        }
        var i;//重新聲明變量
        alert(i);//5
    }

如上代碼所見,i變量定義后在整個(gè)包含函數(shù)中均可訪問余佛。JavaScript中for語句并不會(huì)形成塊級(jí)作用域柠新,其整個(gè)作用域是包含函數(shù)創(chuàng)建的,而且對(duì)變量的后續(xù)聲明都將被忽略辉巡。
要達(dá)到塊級(jí)作用域效果恨憎,我們可以形成閉包來模仿之,如:


    (function() {
        //塊級(jí)作用域
    })()
  1. 添加私有變量或函數(shù)

通過在私有作用域定義私有變量或函數(shù)郊楣,可以形成私有成員憔恳,如:


    (function() {
        var name = 'xjg';
        function getName() {
            reutn name;
        }
        Person = function(val) {
            name = val;
        }
        Person.getName = function() {
            return name;
        }
    })();
    var p1 = new Person('Anagle');
    alert(p1.getName());//Anagle
    alert(getName())//ReferenceError: getName is not defined

此處,name就變成了一個(gè)靜態(tài)私有變量净蚤。

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

局部變量本來在函數(shù)退出時(shí)被銷毀钥组,然而閉包中不是這樣,局部變量生命周期被延長(zhǎng)今瀑,閉包將使這些數(shù)據(jù)無法及時(shí)銷毀程梦,會(huì)占用內(nèi)存,容易造成內(nèi)存泄漏橘荠。如:


    function addHandle() {
        var element = document.getElementById('myNode');
        element.onclick = function() {
            alert(element.id);
        }
    }

此處屿附,onclick匿名函數(shù)保存了一個(gè)對(duì)包含函數(shù)活動(dòng)對(duì)象(變量對(duì)象)的引用,其保存element的引用砾医,element將不會(huì)被回收拿撩。


        function addHandle() {
        var element = document.getElementById('myNode');
        var id = element.id;
        element.onclick = function() {
            alert(id);
        }
        element = null;
    }

此處將element設(shè)為null,即解除對(duì)其的引用如蚜,垃圾回收器將回收其占用內(nèi)存压恒。

此篇對(duì)JavaScript閉包做了總結(jié)影暴,闡述,限于篇幅探赫,在下篇講述JavaScript中的高階函數(shù)型宙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伦吠,隨后出現(xiàn)的幾起案子妆兑,更是在濱河造成了極大的恐慌,老刑警劉巖毛仪,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搁嗓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡箱靴,警方通過查閱死者的電腦和手機(jī)腺逛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衡怀,“玉大人棍矛,你說我怎么就攤上這事∨籽睿” “怎么了够委?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)怖现。 經(jīng)常有香客問我茁帽,道長(zhǎng),這世上最難降的妖魔是什么屈嗤? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任脐雪,我火速辦了婚禮,結(jié)果婚禮上恢共,老公的妹妹穿的比我還像新娘。我一直安慰自己璧亚,他們只是感情好讨韭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著癣蟋,像睡著了一般透硝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疯搅,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天濒生,我揣著相機(jī)與錄音,去河邊找鬼幔欧。 笑死罪治,一個(gè)胖子當(dāng)著我的面吹牛丽声,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播觉义,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雁社,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了晒骇?” 一聲冷哼從身側(cè)響起霉撵,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洪囤,沒想到半個(gè)月后徒坡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘤缩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年喇完,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片款咖。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡何暮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铐殃,到底是詐尸還是另有隱情海洼,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布富腊,位于F島的核電站坏逢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏赘被。R本人自食惡果不足惜是整,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望民假。 院中可真熱鬧浮入,春花似錦、人聲如沸羊异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)野舶。三九已至易迹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間平道,已是汗流浹背睹欲。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窘疮。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓袋哼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親考余。 傳聞我的和親對(duì)象是個(gè)殘疾皇子先嬉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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

  • 作用域和閉包是 JavaScript 最重要的概念之一,想要進(jìn)一步學(xué)習(xí) JavaScript楚堤,就必須理解 Java...
    劼哥stone閱讀 1,177評(píng)論 1 13
  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn)疫蔓,也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)身冬。 一衅胀、變量...
    zock閱讀 1,075評(píng)論 2 6
  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色酥筝,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)滚躯。 一、變量...
    zouCode閱讀 1,271評(píng)論 0 13
  • 我曾夢(mèng)見獨(dú)自一人傲游城市與山谷嘿歌, 也曾夢(mèng)見三五成群譚天說地游戲人生 亦曾夢(mèng)見奇形怪獸肆虐人間 現(xiàn)始終希望每晚有夢(mèng) ...
    生如夏花丶閱讀 194評(píng)論 1 0
  • 《親婆》的故事掸掏,就像開篇作者說的:親婆是個(gè)很普通的老人,記憶里的故事和場(chǎng)景宙帝,也都平平常常丧凤。我想,人間的親情步脓,大概就...
    祺妙媽咪閱讀 3,687評(píng)論 0 6