<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0

    原文

    http://coolshell.cn/articles/6731.html


    前言:還是一篇入門文章。Javascript中有幾個非常重要的語言特性——對象、原型繼承、閉包。其中閉包 對于那些使用傳統靜態語言C/C++的程序員來說是一個新的語言特性。本文將以例子入手來介紹Javascript閉包的語言特性,并結合一點 ECMAScript語言規范來使讀者可以更深入的理解閉包。

    注:本文是入門文章,例子素材整理于網絡,如果你是高手,歡迎針對文章提出技術性建議和意見。本文討論的是Javascript,不想做語言對比,如果您對Javascript天生不適,請自行繞道。

    什么是閉包

    閉包是什么?閉包是Closure,這是靜態語言所不具有的一個新特性。但是閉包也不是什么復雜到不可理解的東西,簡而言之,閉包就是:

    • 閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。
    • 閉包就是就是函數的“堆棧”在函數返回后并不釋放,我們也可以理解為這些函數堆棧并不在棧上分配而是在堆上分配
    • 當在一個函數內定義另外一個函數就會產生閉包

    上面的第二定義是第一個補充說明,抽取第一個定義的主謂賓——閉包是函數的‘局部變量’集合。只是這個局部變量是可以在函數返回后被訪問。(這個不是官方定義,但是這個定義應該更有利于你理解閉包)

    做為局部變量都可以被函數內的代碼訪問,這個和靜態語言是沒有差別。閉包的差別在于局部變變量可以在函數執行結束后仍然被函數外的代碼訪問。這意味 著函數必須返回一個指向閉包的“引用”,或將這個”引用”賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問。當然包含這個引用的實體應該是一個 對象,因為在Javascript中除了基本類型剩下的就都是對象了。可惜的是,ECMAScript并沒有提供相關的成員和方法來訪問閉包中的局部變 量。但是在ECMAScript中,函數對象中定義的內部函數(inner function)是可以直接訪問外部函數的局部變量,通過這種機制,我們就可以以如下的方式完成對閉包的訪問了。

    1
    2
    3
    4
    5
    6
    7
    function greeting(name) {
        var text = 'Hello ' + name; // local variable
        // 每次調用時,產生閉包,并返回內部函數對象給調用者
        return function() { alert(text); }
    }
    var sayHello=greeting("Closure");
    sayHello()  // 通過閉包訪問到了局部變量text

    上述代碼的執行結果是:Hello Closure,因為sayHello()函數在greeting函數執行完畢后,仍然可以訪問到了定義在其之內的局部變量text。

    好了,這個就是傳說中閉包的效果,閉包在Javascript中有多種應用場景和模式,比如Singleton,Power Constructor等這些Javascript模式都離不開對閉包的使用。

    ECMAScript閉包模型

    ECMAScript到底是如何實現閉包的呢?想深入了解的親們可以獲取ECMAScript 規范進行研究,我這里也只做一個簡單的講解,內容也是來自于網絡。

    在ECMAscript的腳本的函數運行時,每個函數關聯都有一個執行上下文場景(Execution Context) ,這個執行上下文場景中包含三個部分

    • 文法環境(The LexicalEnvironment)
    • 變量環境(The VariableEnvironment)
    • this綁定

    其中第三點this綁定與閉包無關,不在本文中討論。文法環境中用于解析函數執行過程使用到的變量標識符。我們可以將文法環境想象成一個對象,該對 象包含了兩個重要組件,環境記錄(Enviroment Recode),和外部引用(指針)。環境記錄包含包含了函數內部聲明的局部變量和參數變量,外部引用指向了外部函數對象的上下文執行場景。全局的上下文 場景中此引用值為NULL。這樣的數據結構就構成了一個單向的鏈表,每個引用都指向外層的上下文場景。

    例如上面我們例子的閉包模型應該是這樣,sayHello函數在最下層,上層是函數greeting,最外層是全局場景。如下圖:

    因此當sayHello被調用的時候,sayHello會通過上下文場景找到局部變量text的值,因此在屏幕的對話框中顯示出”Hello Closure”
    變量環境(The VariableEnvironment)和文法環境的作用基本相似,具體的區別請參看ECMAScript的規范文檔。

    閉包的樣列

    前面的我大致了解了Javascript閉包是什么,閉包在Javascript是怎么實現的。下面我們通過針對一些例子來幫助大家更加深入的理解閉包,下面共有5個樣例,例子來自于JavaScript Closures For Dummies(鏡像)
    例子1:閉包中局部變量是引用而非拷貝

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function say667() {
        // Local variable that ends up within closure
        var num = 666;
        var sayAlert = function() { alert(num); }
        num++;
        return sayAlert;
    }
     
    var sayAlert = say667();
    sayAlert()

    因此執行結果應該彈出的667而非666。

    例子2:多個函數綁定同一個閉包,因為他們定義在同一個函數內。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function setupSomeGlobals() {
        // Local variable that ends up within closure
        var num = 666;
        // Store some references to functions as global variables
        gAlertNumber = function() { alert(num); }
        gIncreaseNumber = function() { num++; }
        gSetNumber = function(x) { num = x; }
    }
    setupSomeGlobals(); // 為三個全局變量賦值
    gAlertNumber(); //666
    gIncreaseNumber();
    gAlertNumber(); // 667
    gSetNumber(12);//
    gAlertNumber();//12

    例子3:當在一個循環中賦值函數時,這些函數將綁定同樣的閉包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function buildList(list) {
        var result = [];
        for (var i = 0; i < list.length; i++) {
            var item = 'item' + list[i];
            result.push( function() {alert(item + ' ' + list[i])} );
        }
        return result;
    }
     
    function testList() {
        var fnlist = buildList([1,2,3]);
        // using j only to help prevent confusion - could use i
        for (var j = 0; j < fnlist.length; j++) {
            fnlist[j]();
        }
    }

    testList的執行結果是彈出item3 undefined窗口三次,因為這三個函數綁定了同一個閉包,而且item的值為最后計算的結果,但是當i跳出循環時i值為4,所以list[4]的結果為undefined.

    例子4:外部函數所有局部變量都在閉包內,即使這個變量聲明在內部函數定義之后。

    1
    2
    3
    4
    5
    6
    7
    8
    function sayAlice() {
        var sayAlert = function() { alert(alice); }
        // Local variable that ends up within closure
        var alice = 'Hello Alice';
        return sayAlert;
    }
    var helloAlice=sayAlice();
    helloAlice();

    執行結果是彈出”Hello Alice”的窗口。即使局部變量聲明在函數sayAlert之后,局部變量仍然可以被訪問到。

    例子5:每次函數調用的時候創建一個新的閉包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function newClosure(someNum, someRef) {
        // Local variables that end up within closure
        var num = someNum;
        var anArray = [1,2,3];
        var ref = someRef;
        return function(x) {
            num += x;
            anArray.push(num);
            alert('num: ' + num +
            '\nanArray ' + anArray.toString() +
            '\nref.someVar ' + ref.someVar);
        }
    }
    closure1=newClosure(40,{someVar:'closure 1'});
    closure2=newClosure(1000,{someVar:'closure 2'});
     
    closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
    closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'

    閉包的應用

    Singleton 單件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var singleton = function () {
        var privateVariable;
        function privateFunction(x) {
            ...privateVariable...
        }
     
        return {
            firstMethod: function (a, b) {
                ...privateVariable...
            },
            secondMethod: function (c) {
                ...privateFunction()...
            }
        };
    }();

    這個單件通過閉包來實現。通過閉包完成了私有的成員和方法的封裝。匿名主函數返回一個對象。對象包含了兩個方法,方法1可以方法私有變量,方法2訪 問內部私有函數。需要注意的地方是匿名主函數結束的地方的’()’,如果沒有這個’()’就不能產生單件。因為匿名函數只能返回了唯一的對象,而且不能被 其他地方調用。這個就是利用閉包產生單件的方法。

    posted on 2012-03-09 11:39 SIMONE 閱讀(451) 評論(0)  編輯  收藏 所屬分類: JavaScript
    主站蜘蛛池模板: 久久精品国产精品亚洲艾 | 亚洲人成色在线观看| 最近2022中文字幕免费视频| 亚洲五月综合缴情在线观看| 黄色视频在线免费观看| 精品国产人成亚洲区| 国产精品成人69XXX免费视频| 亚洲国产a级视频| 特色特黄a毛片高清免费观看| 亚洲欧洲自拍拍偷精品 美利坚| 无人视频在线观看免费播放影院| 免费看一级做a爰片久久| 免费一级特黄特色大片| 亚洲视频在线一区二区| 久久成人永久免费播放| 亚洲第一AV网站| 久久久久久夜精品精品免费啦| 久久精品国产亚洲AV高清热| 亚洲一区二区三区免费视频| 亚洲第一区二区快射影院| 日本一道本高清免费| 九九九精品视频免费| 亚洲国产精品无码中文字| 久久九九兔免费精品6| 在线精品亚洲一区二区| 亚洲日本一区二区三区在线不卡| a级片在线免费看| 亚洲人成人77777网站不卡| 国产一区二区三区在线免费观看| 国产三级在线免费观看| 亚洲高清不卡视频| 西西大胆无码视频免费| 人人爽人人爽人人片av免费| 亚洲国产人成网站在线电影动漫| 最近中文字幕mv免费高清电影| 亚洲.国产.欧美一区二区三区| 亚洲色欲久久久综合网| 99re热免费精品视频观看 | 精品一区二区三区无码免费视频 | 久久精品a亚洲国产v高清不卡| 免费毛片在线看片免费丝瓜视频|