• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運(yùn)轉(zhuǎn),開(kāi)心的工作
            簡(jiǎn)單、開(kāi)放、平等的公司文化;尊重個(gè)性、自由與個(gè)人價(jià)值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            JavaScript面向?qū)ο蟮闹С?3)

            Posted on 2009-09-15 09:26 S.l.e!ep.¢% 閱讀(353) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): HTML
            JavaScript面向?qū)ο蟮闹С?3)
            2008-05-25 22:56
            然而再接下來(lái),我們發(fā)現(xiàn)MyObject2()的實(shí)例obj2的constructor仍然指向function MyObject()。
            盡管這很說(shuō)不通,然而現(xiàn)實(shí)的確如此。——這到底是為什么呢?

            事實(shí)上,僅下面的代碼:
            --------
            function MyObject2() {
            }

            obj2 = new MyObject2();
            document.writeln(MyObject2.prototype.constructor === MyObject2);
            --------
            構(gòu)造的obj2.constructor將正確的指向function MyObject2()。事實(shí)上,我們也會(huì)注意到這
            種情況下,MyObject2的原型屬性的constructor也正確的指向該函數(shù)。然而,由于JavaScript
            要求指定prototype對(duì)象來(lái)構(gòu)造原型鏈:
            --------
            function MyObject2() {
            }
            MyObject2.prototype = new MyObject();

            obj2 = new MyObject2();
            --------
            這時(shí),再訪(fǎng)問(wèn)obj2,將會(huì)得到新的原型(也就是MyObject2.prototype)的constructor屬性。
            因此,一切很明了:原型的屬性影響到構(gòu)造過(guò)程對(duì)對(duì)象的constructor的初始設(shè)定。

            作為一種補(bǔ)充的解決問(wèn)題的手段,JavaScript開(kāi)發(fā)規(guī)范中說(shuō)“need to remember to reset
            the constructor property',要求用戶(hù)自行設(shè)定該屬性。

            所以你會(huì)看到更規(guī)范的JavaScript代碼要求這樣書(shū)寫(xiě):
            //---------------------------------------------------------
            // 維護(hù)constructor屬性的規(guī)范代碼
            //---------------------------------------------------------
            function MyObject2() {
            }
            MyObject2.prototype = new MyObject();
            MyObject2.prototype.constructor = MyObject2;

            obj2 = new MyObject2();

            更外一種解決問(wèn)題的方法,是在function MyObject()中去重置該值。當(dāng)然,這樣會(huì)使
            得執(zhí)行效率稍低一點(diǎn)點(diǎn):
            //---------------------------------------------------------
            // 維護(hù)constructor屬性的第二種方式
            //---------------------------------------------------------
            function MyObject2() {
            ?? this.constructor = arguments.callee;
            ?? // or, this.constructor = MyObject2;

            ?? // ...
            }
            MyObject2.prototype = new MyObject();

            obj2 = new MyObject2();

            5). 析構(gòu)問(wèn)題
            ------
            JavaScript中沒(méi)有析構(gòu)函數(shù),但卻有“對(duì)象析構(gòu)”的問(wèn)題。也就是說(shuō),盡管我們不
            知道一個(gè)對(duì)象什么時(shí)候會(huì)被析構(gòu),也不能截獲它的析構(gòu)過(guò)程并處理一些事務(wù)。然而,
            在一些不多見(jiàn)的時(shí)候,我們會(huì)遇到“要求一個(gè)對(duì)象立即析構(gòu)”的問(wèn)題。

            問(wèn)題大多數(shù)的時(shí)候出現(xiàn)在對(duì)ActiveX Object的處理上。因?yàn)槲覀兛赡茉贘avaScript
            里創(chuàng)建了一個(gè)ActiveX Object,在做完一些處理之后,我們又需要再創(chuàng)建一個(gè)。而
            如果原來(lái)的對(duì)象供應(yīng)者(Server)不允許創(chuàng)建多個(gè)實(shí)例,那么我們就需要在JavaScript
            中確保先前的實(shí)例是已經(jīng)被釋放過(guò)了。接下來(lái),即使Server允許創(chuàng)建多個(gè)實(shí)例,而
            在多個(gè)實(shí)例間允許共享數(shù)據(jù)(例如OS的授權(quán),或者資源、文件的鎖),那么我們?cè)谛?br />實(shí)例中的操作就可能會(huì)出問(wèn)題。

            可能還是有人不明白我們?cè)谡f(shuō)什么,那么我就舉一個(gè)例子:如果創(chuàng)建一個(gè)Excel對(duì)象,
            打開(kāi)文件A,然后我們save它,然后關(guān)閉這個(gè)實(shí)例。然后我們?cè)賱?chuàng)建Excel對(duì)象并打開(kāi)
            同一文件。——注意這時(shí)JavaScript可能還沒(méi)有來(lái)得及析構(gòu)前一個(gè)對(duì)象。——這時(shí)我們
            再想Save這個(gè)文件,就發(fā)現(xiàn)失敗了。下面的代碼示例這種情況:
            //---------------------------------------------------------
            // JavaScript中的析構(gòu)問(wèn)題(ActiveX Object示例)
            //---------------------------------------------------------
            <script>
            var strSaveLocation = 'file:///E:/1.xls'

            function createXLS() {
            ?? var excel = new ActiveXObject("Excel.Application");
            ?? var wk = excel.Workbooks.Add();
            ?? wk.SaveAs(strSaveLocation);
            ?? wk.Saved = true;

            ?? excel.Quit();
            }

            function writeXLS() {
            ?? var excel = new ActiveXObject("Excel.Application");
            ?? var wk = excel.Workbooks.Open(strSaveLocation);
            ?? var sheet = wk.Worksheets(1);
            ?? sheet.Cells(1, 1).Value = '測(cè)試字符串';
            ?? wk.SaveAs(strSaveLocation);
            ?? wk.Saved = true;

            ?? excel.Quit();
            }
            </script>
            <body>
            ?? <button onclick="createXLS()">創(chuàng)建</button>
            ?? <button onclick="writeXLS()">重寫(xiě)</button>
            </body>

            在這個(gè)例子中,在本地文件操作時(shí)并不會(huì)出現(xiàn)異常。——最多只是有一些內(nèi)存垃
            圾而已。然而,如果strSaveLocation是一個(gè)遠(yuǎn)程的URL,這時(shí)本地將會(huì)保存一個(gè)
            文件存取權(quán)限的憑證,而且同時(shí)只能一個(gè)(遠(yuǎn)程的)實(shí)例來(lái)開(kāi)啟該excel文檔并存
            儲(chǔ)。于是如果反復(fù)點(diǎn)擊"重寫(xiě)"按鈕,就會(huì)出現(xiàn)異常。

            ——注意,這是在SPS中操作共享文件時(shí)的一個(gè)實(shí)例的簡(jiǎn)化代碼。因此,它并非
            “學(xué)術(shù)的”無(wú)聊討論,而且工程中的實(shí)際問(wèn)題。

            解決這個(gè)問(wèn)題的方法很復(fù)雜。它涉及到兩個(gè)問(wèn)題:
            ?? - 本地憑證的釋放
            ?? - ActiveX Object實(shí)例的釋放

            下面我們先從JavaScript中對(duì)象的“失效”問(wèn)題說(shuō)起。簡(jiǎn)單的說(shuō):
            ?? - 一個(gè)對(duì)象在其生存的上下文環(huán)境之外,即會(huì)失效。
            ?? - 一個(gè)全局的對(duì)象在沒(méi)有被執(zhí)用(引用)的情況下,即會(huì)失效。

            例如:
            //---------------------------------------------------------
            // JavaScript對(duì)象何時(shí)失效
            //---------------------------------------------------------
            function testObject() {
            ?? var _obj1 = new Object();
            }

            function testObject2() {
            ?? var _obj2 = new Object();
            ?? return _obj2;
            }

            // 示例1
            testObject();

            // 示例2
            testObject2()

            // 示例3
            var obj3 = testObject2();
            obj3 = null;

            // 示例4
            var obj4 = testObject2();
            var arr = [obj4];
            obj3 = null;
            arr = [];

            在這四個(gè)示例中:
            ?? - “示例1”在函數(shù)testObject()中構(gòu)造了_obj1,但是在函數(shù)退出時(shí),
            ???? 它就已經(jīng)離開(kāi)了函數(shù)的上下文環(huán)境,因此_obj1失效了;
            ?? - “示例2”中,testObject2()中也構(gòu)造了一個(gè)對(duì)象_obj2并傳出,因
            ???? 此對(duì)象有了“函數(shù)外”的上下文環(huán)境(和生存周期),然而由于函數(shù)
            ???? 的返回值沒(méi)有被其它變量“持有”,因此_obj2也立即失效了;
            ?? - “示例3”中,testObject2()構(gòu)造的_obj2被外部的變量obj3持用了,
            ???? 這時(shí),直到“obj3=null”這行代碼生效時(shí),_obj2才會(huì)因?yàn)橐藐P(guān)系
            ???? 消失而失效。
            ?? - 與示例3相同的原因,“示例4”中的_obj2會(huì)在“arr=[]”這行代碼
            ???? 之后才會(huì)失效。

            但是,對(duì)象的“失效”并不等會(huì)“釋放”。在JavaScript運(yùn)行環(huán)境的內(nèi)部,沒(méi)
            有任何方式來(lái)確切地告訴用戶(hù)“對(duì)象什么時(shí)候會(huì)釋放”。這依賴(lài)于JavaScript
            的內(nèi)存回收機(jī)制。——這種策略與.NET中的回收機(jī)制是類(lèi)同的。

            在前面的Excel操作示例代碼中,對(duì)象的所有者,也就是"EXCEL.EXE"這個(gè)進(jìn)程
            只能在“ActiveX Object實(shí)例的釋放”之后才會(huì)發(fā)生。而文件的鎖,以及操作
            系統(tǒng)的權(quán)限憑證是與進(jìn)程相關(guān)的。因此如果對(duì)象僅是“失效”而不是“釋放”,
            那么其它進(jìn)程處理文件和引用操作系統(tǒng)的權(quán)限憑據(jù)時(shí)就會(huì)出問(wèn)題。

            ——有些人說(shuō)這是JavaScript或者COM機(jī)制的BUG。其實(shí)不是,這是OS、IE
            和JavaScript之間的一種復(fù)雜關(guān)系所導(dǎo)致的,而非獨(dú)立的問(wèn)題。

            Microsoft公開(kāi)了解決這種問(wèn)題的策略:主動(dòng)調(diào)用內(nèi)存回收過(guò)程。

            在(微軟的)JScript中提供了一個(gè)CollectGarbage()過(guò)程(通常簡(jiǎn)稱(chēng)GC過(guò)程),
            GC過(guò)程用于清理當(dāng)前IE中的“失效的對(duì)象失例”,也就是調(diào)用對(duì)象的析構(gòu)過(guò)程。

            在上例中調(diào)用GC過(guò)程的代碼是:
            //---------------------------------------------------------
            // 處理ActiveX Object時(shí),GC過(guò)程的標(biāo)準(zhǔn)調(diào)用方式
            //---------------------------------------------------------
            function writeXLS() {
            ?? //(略...)

            ?? excel.Quit();
            ?? excel = null;
            ?? setTimeout(CollectGarbage, 1);
            }

            第一行代碼調(diào)用excel.Quit()方法來(lái)使得excel進(jìn)程中止并退出,這時(shí)由于JavaScript
            環(huán)境執(zhí)有excel對(duì)象實(shí)例,因此excel進(jìn)程并不實(shí)際中止。

            第二行代碼使excel為null,以清除對(duì)象引用,從而使對(duì)象“失效”。然而由于
            對(duì)象仍舊在函數(shù)上下文環(huán)境中,因此如果直接調(diào)用GC過(guò)程,對(duì)象仍然不會(huì)被清理。

            第三行代碼使用setTimeout()來(lái)調(diào)用CollectGarbage函數(shù),時(shí)間間隔設(shè)為'1',只
            是使得GC過(guò)程發(fā)生在writeXLS()函數(shù)執(zhí)行完之后。這樣excel對(duì)象就滿(mǎn)足了“能被
            GC清理”的兩個(gè)條件:沒(méi)有引用和離開(kāi)上下文環(huán)境。

            GC過(guò)程的使用,在使用了ActiveX Object的JS環(huán)境中很有效。一些潛在的ActiveX
            Object包括XML、VML、OWC(Office Web Componet)、flash,甚至包括在JS中的VBArray。
            從這一點(diǎn)來(lái)看,ajax架構(gòu)由于采用了XMLHTTP,并且同時(shí)要滿(mǎn)足“不切換頁(yè)面”的
            特性,因此在適當(dāng)?shù)臅r(shí)候主動(dòng)調(diào)用GC過(guò)程,會(huì)得到更好的效率用UI體驗(yàn)。

            事實(shí)上,即使使用GC過(guò)程,前面提到的excel問(wèn)題仍然不會(huì)被完全解決。因?yàn)镮E還
            緩存了權(quán)限憑據(jù)。使頁(yè)的權(quán)限憑據(jù)被更新的唯一方法,只能是“切換到新的頁(yè)面”,
            因此事實(shí)上在前面提到的那個(gè)SPS項(xiàng)目中,我采用的方法并不是GC,而是下面這一
            段代碼:
            //---------------------------------------------------------
            // 處理ActiveX Object時(shí)采用的頁(yè)面切換代碼
            //---------------------------------------------------------
            function writeXLS() {
            ?? //(略...)

            ?? excel.Quit();
            ?? excel = null;

            ?? // 下面代碼用于解決IE call Excel的一個(gè)BUG, MSDN中提供的方法:
            ?? //??? setTimeout(CollectGarbage, 1);
            ?? // 由于不能清除(或同步)網(wǎng)頁(yè)的受信任狀態(tài), 所以將導(dǎo)致SaveAs()等方法在
            ?? // 下次調(diào)用時(shí)無(wú)效.
            ?? location.reload();
            }

            最后之最后,關(guān)于GC的一個(gè)補(bǔ)充說(shuō)明:在IE窗體被最小化時(shí),IE將會(huì)主動(dòng)調(diào)用一次
            CollectGarbage()函數(shù)。這使得IE窗口在最小化之后,內(nèi)存占用會(huì)有明顯改善。

            八、JavaScript面向?qū)ο蟮闹С?br />~~~~~~~~~~~~~~~~~~
            (續(xù))

            4. 實(shí)例和實(shí)例引用
            --------
            在.NET Framework對(duì)CTS(Common Type System)約定“一切都是對(duì)象”,并分為“值類(lèi)型”和“引用類(lèi)型”兩種。其中“值類(lèi)型”的對(duì)象在轉(zhuǎn)換成“引用類(lèi)型”數(shù)據(jù)的過(guò)程中,需要進(jìn)行一個(gè)“裝箱”和“拆箱”的過(guò)程。

            在JavaScript也有同樣的問(wèn)題。我們看到的typeof關(guān)鍵字,返回以下六種數(shù)據(jù)類(lèi)型:
            "number"、"string"、"boolean"、"object"、"function" 和 "undefined"。

            我們也發(fā)現(xiàn)JavaScript的對(duì)象系統(tǒng)中,有String、Number、Function、Boolean這四種對(duì)象構(gòu)造器。那么,我們的問(wèn)題是:如果有一個(gè)數(shù)字A,typeof(A)的結(jié)果,到底會(huì)是'number'呢,還是一個(gè)構(gòu)造器指向function Number()的對(duì)象呢?

            //---------------------------------------------------------
            // 關(guān)于JavaScript的類(lèi)型的測(cè)試代碼
            //---------------------------------------------------------
            function getTypeInfo(V) {
            ?? return (typeof V == 'object' ??? 'Object, construct by '+V.constructor
            ??? : 'Value, type of '+typeof V);
            }

            var A1 = 100;
            var A2 = new Number(100);

            document.writeln('A1 is ', getTypeInfo(A1), '<BR>');
            document.writeln('A2 is ', getTypeInfo(A2), '<BR>');
            document.writeln([A1.constructor === A2.constructor, A2.constructor === Number]);

            測(cè)試代碼的執(zhí)行結(jié)果如下:
            -----------
            A1 is Value, type of number
            A2 is Object, construct by function Number() { [native code] }
            true,true
            -----------

            我們注意到,A1和A2的構(gòu)造器都指向Number。這意味著通過(guò)constructor屬性來(lái)識(shí)別對(duì)象,(有時(shí))比typeof更加有效。因?yàn)椤爸殿?lèi)型數(shù)據(jù)”A1作為一個(gè)對(duì)象來(lái)看待時(shí),與A2有完全相同的特性。

            ——除了與實(shí)例引用有關(guān)的問(wèn)題。

            參考JScript手冊(cè),我們對(duì)其它基礎(chǔ)類(lèi)型和構(gòu)造器做相同考察,可以發(fā)現(xiàn):
            ?? - 基礎(chǔ)類(lèi)型中的undefined、number、boolean和string,是“值類(lèi)型”變量
            ?? - 基礎(chǔ)類(lèi)型中的array、function和object,是“引用類(lèi)型”變量
            ?? - 使用new()方法構(gòu)造出對(duì)象,是“引用類(lèi)型”變量

            下面的代碼說(shuō)明“值類(lèi)型”與“引用類(lèi)型”之間的區(qū)別:
            //---------------------------------------------------------
            // 關(guān)于JavaScript類(lèi)型系統(tǒng)中的值/引用問(wèn)題
            //---------------------------------------------------------
            var str1 = 'abcdefgh', str2 = 'abcdefgh';
            var obj1 = new String('abcdefgh'), obj2 = new String('abcdefgh');

            document.writeln([str1==str2, str1===str2], '<br>');
            document.writeln([obj1==obj2, obj1===obj2]);

            測(cè)試代碼的執(zhí)行結(jié)果如下:
            -----------
            true, true
            false, false
            -----------

            我們看到,無(wú)論是等值運(yùn)算(==),還是全等運(yùn)算(===),對(duì)“對(duì)象”和“值”的理解都是不一樣的。

            更進(jìn)一步的理解這種現(xiàn)象,我們知道:
            ?? - 運(yùn)算結(jié)果為值類(lèi)型,或變量為值類(lèi)型時(shí),等值(或全等)比較可以得到預(yù)想結(jié)果
            ?? - (即使包含相同的數(shù)據(jù),)不同的對(duì)象實(shí)例之間是不等值(或全等)的
            ?? - 同一個(gè)對(duì)象的不同引用之間,是等值(==)且全等(===)的

            但對(duì)于String類(lèi)型,有一點(diǎn)補(bǔ)充:根據(jù)JScript的描述,兩個(gè)字符串比較時(shí),只要有一個(gè)是值類(lèi)型,則按值比較。這意味著在上面的例子中,代碼“str1==obj1”會(huì)得到結(jié)果true。而全等(===)運(yùn)算需要檢測(cè)變量類(lèi)型的一致性,因此“str1===obj1”的結(jié)果返回false。

            JavaScript中的函數(shù)參數(shù)總是傳入值參,引用類(lèi)型(的實(shí)例)是作為指針值傳入的。因此函數(shù)可以隨意重寫(xiě)入口變量,而不用擔(dān)心外部變量被修改。但是,需要留意傳入的引用類(lèi)型的變量,因?yàn)閷?duì)它方法調(diào)用和屬性讀寫(xiě)可能會(huì)影響到實(shí)例本身。——但,也可以通過(guò)引用類(lèi)型的參數(shù)來(lái)傳出數(shù)據(jù)。

            最后補(bǔ)充說(shuō)明一下,值類(lèi)型比較會(huì)逐字節(jié)檢測(cè)對(duì)象實(shí)例中的數(shù)據(jù),效率低但準(zhǔn)確性高;而引用類(lèi)型只檢測(cè)實(shí)例指針和數(shù)據(jù)類(lèi)型,因此效率高而準(zhǔn)確性低。如果你需要檢測(cè)兩個(gè)引用類(lèi)型是否真的包含相同的數(shù)據(jù),可能你需要嘗試把它轉(zhuǎn)換成“字符串值”再來(lái)比較。

            6. 函數(shù)的上下文環(huán)境
            --------
            只要寫(xiě)過(guò)代碼,你應(yīng)該知道變量是有“全局變量”和“局部變量”之分的。絕大多數(shù)的
            JavaScript程序員也知道下面這些概念:
            //---------------------------------------------------------
            // JavaScript中的全局變量與局部變量
            //---------------------------------------------------------
            var v1 = '全局變量-1';
            v2 = '全局變量-2';

            function foo() {
            ?? v3 = '全局變量-3';

            ?? var v4 = '只有在函數(shù)內(nèi)部并使用var定義的,才是局部變量';
            }

            按照通常對(duì)語(yǔ)言的理解來(lái)說(shuō),不同的代碼調(diào)用函數(shù),都會(huì)擁有一套獨(dú)立的局部變量。
            因此下面這段代碼很容易理解:
            //---------------------------------------------------------
            // JavaScript的局部變量
            //---------------------------------------------------------
            function MyObject() {
            ?? var o = new Object;

            ?? this.getValue = function() {
            ???? return o;
            ?? }
            }

            var obj1 = new MyObject();
            var obj2 = new MyObject();
            document.writeln(obj1.getValue() == obj2.getValue());

            結(jié)果顯示false,表明不同(實(shí)例的方法)調(diào)用返回的局部變量“obj1/obj2”是不相同。

            變量的局部、全局特性與OOP的封裝性中的“私有(private)”、“公開(kāi)(public)”具有類(lèi)同性。因此絕大多數(shù)資料總是以下面的方式來(lái)說(shuō)明JavaScript的面向?qū)ο笙到y(tǒng)中的“封裝權(quán)限級(jí)別”問(wèn)題:
            //---------------------------------------------------------
            // JavaScript中OOP封裝性
            //---------------------------------------------------------
            function MyObject() {
            ?? // 1. 私有成員和方法
            ?? var private_prop = 0;
            ?? var private_method_1 = function() {
            ???? // ...
            ???? return 1
            ?? }
            ?? function private_method_2() {
            ???? // ...
            ???? return 1
            ?? }

            ?? // 2. 特權(quán)方法
            ?? this.privileged_method = function () {
            ???? private_prop++;
            ???? return private_prop + private_method_1() + private_method_2();
            ?? }

            ?? // 3. 公開(kāi)成員和方法
            ?? this.public_prop_1 = '';
            ?? this.public_method_1 = function () {
            ???? // ...
            ?? }
            }

            // 4. 公開(kāi)成員和方法(2)
            MyObject.prototype.public_prop_1 = '';
            MyObject.prototype.public_method_1 = function () {
            ?? // ...
            }

            var obj1 = new MyObject();
            var obj2 = new MyObject();

            document.writeln(obj1.privileged_method(), '<br>');
            document.writeln(obj2.privileged_method());

            在這里,“私有(private)”表明只有在(構(gòu)造)函數(shù)內(nèi)部可訪(fǎng)問(wèn),而“特權(quán)(privileged)”是特指一種存取“私有域”的“公開(kāi)(public)”方法。“公開(kāi)(public)”表明在(構(gòu)造)函數(shù)外可以調(diào)用和存取。

            除了上述的封裝權(quán)限之外,一些文檔還介紹了其它兩種相關(guān)的概念:
            ?? - 原型屬性:Classname.prototype.propertyName = someValue
            ?? - (類(lèi))靜態(tài)屬性:Classname.propertyName = someValue

            然而,從面向?qū)ο蟮慕嵌壬蟻?lái)講,上面這些概念都很難自圓其說(shuō):JavaScript究竟是為何、以及如何劃分出這些封裝權(quán)限和概念來(lái)的呢?

            ——因?yàn)槲覀儽仨氉⒁獾较旅孢@個(gè)例子所帶來(lái)的問(wèn)題:
            //---------------------------------------------------------
            // JavaScript中的局部變量
            //---------------------------------------------------------
            function MyFoo() {
            ?? var i;

            ?? MyFoo.setValue = function (v) {
            ????? i = v;
            ?? }
            ?? MyFoo.getValue = function () {
            ????? return i;
            ?? }
            }
            MyFoo();

            var obj1 = new Object();
            var obj2 = new Object();

            // 測(cè)試一
            MyFoo.setValue.call(obj1, 'obj1');
            document.writeln(MyFoo.getValue.call(obj1), '<BR>');

            // 測(cè)試二
            MyFoo.setValue.call(obj2, 'obj2');
            document.writeln(MyFoo.getValue.call(obj2));
            document.writeln(MyFoo.getValue.call(obj1));
            document.writeln(MyFoo.getValue());

            在這個(gè)測(cè)試代碼中,obj1/obj2都是Object()實(shí)例。我們使用function.call()的方式來(lái)調(diào)用setValue/getValue,使得在MyFoo()調(diào)用的過(guò)程中替換this為obj1/obj2實(shí)例。

            然而我們發(fā)現(xiàn)“測(cè)試二”完成之后,obj2、obj1以及function MyFoo()所持有的局部變量都返回了“obj2”。——這表明三個(gè)函數(shù)使用了同一個(gè)局部變量。

            由此可見(jiàn),JavaScript在處理局部變量時(shí),對(duì)“普通函數(shù)”與“構(gòu)造器”是分別對(duì)待的。這種處理策略在一些JavaScript相關(guān)的資料中被解釋作“面向?qū)ο笾械乃接杏颉眴?wèn)題。而事實(shí)上,我更愿意從源代碼一級(jí)來(lái)告訴你真相:這是對(duì)象的上下文環(huán)境的問(wèn)題。——只不過(guò)從表面看去,“上下文環(huán)境”的問(wèn)題被轉(zhuǎn)嫁到對(duì)象的封裝性問(wèn)題上了。

            (在閱讀下面的文字之前,)先做一個(gè)概念性的說(shuō)明:
            ?? - 在普通函數(shù)中,上下文環(huán)境被window對(duì)象所持有
            - 在“構(gòu)造器和對(duì)象方法”中,上下文環(huán)境被對(duì)象實(shí)例所持有

            在JavaScript的實(shí)現(xiàn)代碼中,每次創(chuàng)建一個(gè)對(duì)象,解釋器將為對(duì)象創(chuàng)建一個(gè)上下文環(huán)境鏈,用于存放對(duì)象在進(jìn)入“構(gòu)造器和對(duì)象方法”時(shí)對(duì)function()內(nèi)部數(shù)據(jù)的一個(gè)備份。JavaScript保證這個(gè)對(duì)象在以后再進(jìn)入“構(gòu)造器和對(duì)象方法”內(nèi)部時(shí),總是持有該上下文環(huán)境,和一個(gè)與之相關(guān)的this對(duì)象。由于對(duì)象可能有多個(gè)方法,且每個(gè)方法可能又存在多層嵌套函數(shù),因此這事實(shí)上構(gòu)成了一個(gè)上下文環(huán)境的樹(shù)型鏈表結(jié)構(gòu)。而在構(gòu)造器和對(duì)象方法之外,JavaScript不提供任何訪(fǎng)問(wèn)(該構(gòu)造器和對(duì)象方法的)上下文環(huán)境的方法。

            簡(jiǎn)而言之:
            ?? - 上下文環(huán)境與對(duì)象實(shí)例調(diào)用“構(gòu)造器和對(duì)象方法”時(shí)相關(guān),而與(普通)函數(shù)無(wú)關(guān)
            ?? - 上下文環(huán)境記錄一個(gè)對(duì)象在“構(gòu)造函數(shù)和對(duì)象方法”內(nèi)部的私有數(shù)據(jù)
            ?? - 上下文環(huán)境采用鏈?zhǔn)浇Y(jié)構(gòu),以記錄多層的嵌套函數(shù)中的上下文

            由于上下文環(huán)境只與構(gòu)造函數(shù)及其內(nèi)部的嵌套函數(shù)有關(guān),重新閱讀前面的代碼:
            //---------------------------------------------------------
            // JavaScript中的局部變量
            //---------------------------------------------------------
            function MyFoo() {
            ?? var i;

            ?? MyFoo.setValue = function (v) {
            ????? i = v;
            ?? }
            ?? MyFoo.getValue = function () {
            ????? return i;
            ?? }
            }
            MyFoo();

            var obj1 = new Object();
            MyFoo.setValue.call(obj1, 'obj1');

            我們發(fā)現(xiàn)setValue()的確可以訪(fǎng)問(wèn)到位于MyFoo()函數(shù)內(nèi)部的“局部變量i”,但是由于setValue()方法的執(zhí)有者是MyFoo對(duì)象(記住函數(shù)也是對(duì)象),因此MyFoo對(duì)象擁有MyFoo()函數(shù)的唯一一份“上下文環(huán)境”。

            接下來(lái)MyFoo.setValue.call()調(diào)用雖然為setValue()傳入了新的this對(duì)象,但實(shí)際上擁有“上下文環(huán)境”的仍舊是MyFoo對(duì)象。因此我們看到無(wú)論創(chuàng)建多少個(gè)obj1/obj2,最終操作的都是同一個(gè)私有變量i。

            全局函數(shù)/變量的“上下文環(huán)境”持有者為window,因此下面的代碼說(shuō)明了“為什么全局變量能被任意的對(duì)象和函數(shù)訪(fǎng)問(wèn)”:
            //---------------------------------------------------------
            // 全局函數(shù)的上下文
            //---------------------------------------------------------
            /*
            function Window() {
            */
            ?? var global_i = 0;
            ?? var global_j = 1;

            ?? function foo_0() {
            ?? }

            ?? function foo_1() {
            ?? }
            /*
            }

            window = new Window();
            */

            因此我們可以看到foo_0()與foo_1()能同時(shí)訪(fǎng)問(wèn)global_i和global_j。接下來(lái)的推論是,上下文環(huán)境決定了變量的“全局”與“私有”。而不是反過(guò)來(lái)通過(guò)變量的私有與全局來(lái)討論上下文環(huán)境問(wèn)題。

            更進(jìn)一步的推論是:JavaScript中的全局變量與函數(shù),本質(zhì)上是window對(duì)象的私有變量與方法。而這個(gè)上下文環(huán)境塊,位于所有(window對(duì)象內(nèi)部的)對(duì)象實(shí)例的上下文環(huán)境鏈表的頂端,因此都可能訪(fǎng)問(wèn)到。

            用“上下文環(huán)境”的理論,你可以順利地解釋在本小節(jié)中,有關(guān)變量的“全局/局部”作用域的問(wèn)題,以及有關(guān)對(duì)象方法的封裝權(quán)限問(wèn)題。事實(shí)上,在實(shí)現(xiàn)JavaScript的C源代碼中,這個(gè)“上下文環(huán)境”被叫做“JSContext”,并作為函數(shù)/方法的第一個(gè)參數(shù)傳入。——如果你有興趣,你可以從源代碼中證實(shí)本小節(jié)所述的理論。

            另外,《JavaScript權(quán)威指南》這本書(shū)中第4.7節(jié)也講述了這個(gè)問(wèn)題,但被叫做“變量的作用域”。然而重要的是,這本書(shū)把問(wèn)題講反了。——作者試圖用“全局、局部的作用域”,來(lái)解釋產(chǎn)生這種現(xiàn)象的“上下文環(huán)境”的問(wèn)題。因此這個(gè)小節(jié)顯得凌亂而且難以自圓其說(shuō)。
            久久亚洲美女精品国产精品| 久久久久国产精品嫩草影院| 精品国产乱码久久久久久郑州公司| 99精品久久精品一区二区| 精品人妻久久久久久888| 99久久婷婷国产一区二区| 久久国内免费视频| 91精品国产91久久久久福利| 久久精品国产福利国产琪琪 | 久久久久噜噜噜亚洲熟女综合| 亚洲伊人久久成综合人影院| 久久久久久久综合日本亚洲| 久久激情亚洲精品无码?V| 人人狠狠综合久久亚洲| 72种姿势欧美久久久久大黄蕉| 久久久精品国产Sm最大网站| 久久亚洲私人国产精品vA | 国产精品99久久不卡| 久久久久av无码免费网| 久久se这里只有精品| 久久久久久无码Av成人影院| 久久综合久久性久99毛片| 91精品国产乱码久久久久久| 久久午夜夜伦鲁鲁片免费无码影视 | 欧美激情精品久久久久久久九九九| 久久国产热精品波多野结衣AV| 伊人色综合久久天天网| 久久高潮一级毛片免费| 久久电影网一区| 久久人人爽爽爽人久久久| 四虎影视久久久免费观看| 久久国产免费直播| 国产亚洲精品自在久久| 亚洲精品无码久久千人斩| 模特私拍国产精品久久| 天堂无码久久综合东京热| 久久免费香蕉视频| 久久亚洲AV无码西西人体| 国产精品99久久久久久董美香| 91精品日韩人妻无码久久不卡| 国产一久久香蕉国产线看观看 |