• <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.¢%

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

            JavaScript面向對象的支持(3)

            Posted on 2009-09-15 09:26 S.l.e!ep.¢% 閱讀(353) 評論(0)  編輯 收藏 引用 所屬分類: HTML
            JavaScript面向對象的支持(3)
            2008-05-25 22:56
            然而再接下來,我們發現MyObject2()的實例obj2的constructor仍然指向function MyObject()。
            盡管這很說不通,然而現實的確如此。——這到底是為什么呢?

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

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

            obj2 = new MyObject2();
            --------
            這時,再訪問obj2,將會得到新的原型(也就是MyObject2.prototype)的constructor屬性。
            因此,一切很明了:原型的屬性影響到構造過程對對象的constructor的初始設定。

            作為一種補充的解決問題的手段,JavaScript開發規范中說“need to remember to reset
            the constructor property',要求用戶自行設定該屬性。

            所以你會看到更規范的JavaScript代碼要求這樣書寫:
            //---------------------------------------------------------
            // 維護constructor屬性的規范代碼
            //---------------------------------------------------------
            function MyObject2() {
            }
            MyObject2.prototype = new MyObject();
            MyObject2.prototype.constructor = MyObject2;

            obj2 = new MyObject2();

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

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

            obj2 = new MyObject2();

            5). 析構問題
            ------
            JavaScript中沒有析構函數,但卻有“對象析構”的問題。也就是說,盡管我們不
            知道一個對象什么時候會被析構,也不能截獲它的析構過程并處理一些事務。然而,
            在一些不多見的時候,我們會遇到“要求一個對象立即析構”的問題。

            問題大多數的時候出現在對ActiveX Object的處理上。因為我們可能在JavaScript
            里創建了一個ActiveX Object,在做完一些處理之后,我們又需要再創建一個。而
            如果原來的對象供應者(Server)不允許創建多個實例,那么我們就需要在JavaScript
            中確保先前的實例是已經被釋放過了。接下來,即使Server允許創建多個實例,而
            在多個實例間允許共享數據(例如OS的授權,或者資源、文件的鎖),那么我們在新
            實例中的操作就可能會出問題。

            可能還是有人不明白我們在說什么,那么我就舉一個例子:如果創建一個Excel對象,
            打開文件A,然后我們save它,然后關閉這個實例。然后我們再創建Excel對象并打開
            同一文件。——注意這時JavaScript可能還沒有來得及析構前一個對象。——這時我們
            再想Save這個文件,就發現失敗了。下面的代碼示例這種情況:
            //---------------------------------------------------------
            // JavaScript中的析構問題(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 = '測試字符串';
            ?? wk.SaveAs(strSaveLocation);
            ?? wk.Saved = true;

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

            在這個例子中,在本地文件操作時并不會出現異常。——最多只是有一些內存垃
            圾而已。然而,如果strSaveLocation是一個遠程的URL,這時本地將會保存一個
            文件存取權限的憑證,而且同時只能一個(遠程的)實例來開啟該excel文檔并存
            儲。于是如果反復點擊"重寫"按鈕,就會出現異常。

            ——注意,這是在SPS中操作共享文件時的一個實例的簡化代碼。因此,它并非
            “學術的”無聊討論,而且工程中的實際問題。

            解決這個問題的方法很復雜。它涉及到兩個問題:
            ?? - 本地憑證的釋放
            ?? - ActiveX Object實例的釋放

            下面我們先從JavaScript中對象的“失效”問題說起。簡單的說:
            ?? - 一個對象在其生存的上下文環境之外,即會失效。
            ?? - 一個全局的對象在沒有被執用(引用)的情況下,即會失效。

            例如:
            //---------------------------------------------------------
            // JavaScript對象何時失效
            //---------------------------------------------------------
            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 = [];

            在這四個示例中:
            ?? - “示例1”在函數testObject()中構造了_obj1,但是在函數退出時,
            ???? 它就已經離開了函數的上下文環境,因此_obj1失效了;
            ?? - “示例2”中,testObject2()中也構造了一個對象_obj2并傳出,因
            ???? 此對象有了“函數外”的上下文環境(和生存周期),然而由于函數
            ???? 的返回值沒有被其它變量“持有”,因此_obj2也立即失效了;
            ?? - “示例3”中,testObject2()構造的_obj2被外部的變量obj3持用了,
            ???? 這時,直到“obj3=null”這行代碼生效時,_obj2才會因為引用關系
            ???? 消失而失效。
            ?? - 與示例3相同的原因,“示例4”中的_obj2會在“arr=[]”這行代碼
            ???? 之后才會失效。

            但是,對象的“失效”并不等會“釋放”。在JavaScript運行環境的內部,沒
            有任何方式來確切地告訴用戶“對象什么時候會釋放”。這依賴于JavaScript
            的內存回收機制。——這種策略與.NET中的回收機制是類同的。

            在前面的Excel操作示例代碼中,對象的所有者,也就是"EXCEL.EXE"這個進程
            只能在“ActiveX Object實例的釋放”之后才會發生。而文件的鎖,以及操作
            系統的權限憑證是與進程相關的。因此如果對象僅是“失效”而不是“釋放”,
            那么其它進程處理文件和引用操作系統的權限憑據時就會出問題。

            ——有些人說這是JavaScript或者COM機制的BUG。其實不是,這是OS、IE
            和JavaScript之間的一種復雜關系所導致的,而非獨立的問題。

            Microsoft公開了解決這種問題的策略:主動調用內存回收過程。

            在(微軟的)JScript中提供了一個CollectGarbage()過程(通常簡稱GC過程),
            GC過程用于清理當前IE中的“失效的對象失例”,也就是調用對象的析構過程。

            在上例中調用GC過程的代碼是:
            //---------------------------------------------------------
            // 處理ActiveX Object時,GC過程的標準調用方式
            //---------------------------------------------------------
            function writeXLS() {
            ?? //(略...)

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

            第一行代碼調用excel.Quit()方法來使得excel進程中止并退出,這時由于JavaScript
            環境執有excel對象實例,因此excel進程并不實際中止。

            第二行代碼使excel為null,以清除對象引用,從而使對象“失效”。然而由于
            對象仍舊在函數上下文環境中,因此如果直接調用GC過程,對象仍然不會被清理。

            第三行代碼使用setTimeout()來調用CollectGarbage函數,時間間隔設為'1',只
            是使得GC過程發生在writeXLS()函數執行完之后。這樣excel對象就滿足了“能被
            GC清理”的兩個條件:沒有引用和離開上下文環境。

            GC過程的使用,在使用了ActiveX Object的JS環境中很有效。一些潛在的ActiveX
            Object包括XML、VML、OWC(Office Web Componet)、flash,甚至包括在JS中的VBArray。
            從這一點來看,ajax架構由于采用了XMLHTTP,并且同時要滿足“不切換頁面”的
            特性,因此在適當的時候主動調用GC過程,會得到更好的效率用UI體驗。

            事實上,即使使用GC過程,前面提到的excel問題仍然不會被完全解決。因為IE還
            緩存了權限憑據。使頁的權限憑據被更新的唯一方法,只能是“切換到新的頁面”,
            因此事實上在前面提到的那個SPS項目中,我采用的方法并不是GC,而是下面這一
            段代碼:
            //---------------------------------------------------------
            // 處理ActiveX Object時采用的頁面切換代碼
            //---------------------------------------------------------
            function writeXLS() {
            ?? //(略...)

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

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

            最后之最后,關于GC的一個補充說明:在IE窗體被最小化時,IE將會主動調用一次
            CollectGarbage()函數。這使得IE窗口在最小化之后,內存占用會有明顯改善。

            八、JavaScript面向對象的支持
            ~~~~~~~~~~~~~~~~~~
            (續)

            4. 實例和實例引用
            --------
            在.NET Framework對CTS(Common Type System)約定“一切都是對象”,并分為“值類型”和“引用類型”兩種。其中“值類型”的對象在轉換成“引用類型”數據的過程中,需要進行一個“裝箱”和“拆箱”的過程。

            在JavaScript也有同樣的問題。我們看到的typeof關鍵字,返回以下六種數據類型:
            "number"、"string"、"boolean"、"object"、"function" 和 "undefined"。

            我們也發現JavaScript的對象系統中,有String、Number、Function、Boolean這四種對象構造器。那么,我們的問題是:如果有一個數字A,typeof(A)的結果,到底會是'number'呢,還是一個構造器指向function Number()的對象呢?

            //---------------------------------------------------------
            // 關于JavaScript的類型的測試代碼
            //---------------------------------------------------------
            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]);

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

            我們注意到,A1和A2的構造器都指向Number。這意味著通過constructor屬性來識別對象,(有時)比typeof更加有效。因為“值類型數據”A1作為一個對象來看待時,與A2有完全相同的特性。

            ——除了與實例引用有關的問題。

            參考JScript手冊,我們對其它基礎類型和構造器做相同考察,可以發現:
            ?? - 基礎類型中的undefined、number、boolean和string,是“值類型”變量
            ?? - 基礎類型中的array、function和object,是“引用類型”變量
            ?? - 使用new()方法構造出對象,是“引用類型”變量

            下面的代碼說明“值類型”與“引用類型”之間的區別:
            //---------------------------------------------------------
            // 關于JavaScript類型系統中的值/引用問題
            //---------------------------------------------------------
            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]);

            測試代碼的執行結果如下:
            -----------
            true, true
            false, false
            -----------

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

            更進一步的理解這種現象,我們知道:
            ?? - 運算結果為值類型,或變量為值類型時,等值(或全等)比較可以得到預想結果
            ?? - (即使包含相同的數據,)不同的對象實例之間是不等值(或全等)的
            ?? - 同一個對象的不同引用之間,是等值(==)且全等(===)的

            但對于String類型,有一點補充:根據JScript的描述,兩個字符串比較時,只要有一個是值類型,則按值比較。這意味著在上面的例子中,代碼“str1==obj1”會得到結果true。而全等(===)運算需要檢測變量類型的一致性,因此“str1===obj1”的結果返回false。

            JavaScript中的函數參數總是傳入值參,引用類型(的實例)是作為指針值傳入的。因此函數可以隨意重寫入口變量,而不用擔心外部變量被修改。但是,需要留意傳入的引用類型的變量,因為對它方法調用和屬性讀寫可能會影響到實例本身。——但,也可以通過引用類型的參數來傳出數據。

            最后補充說明一下,值類型比較會逐字節檢測對象實例中的數據,效率低但準確性高;而引用類型只檢測實例指針和數據類型,因此效率高而準確性低。如果你需要檢測兩個引用類型是否真的包含相同的數據,可能你需要嘗試把它轉換成“字符串值”再來比較。

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

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

            ?? var v4 = '只有在函數內部并使用var定義的,才是局部變量';
            }

            按照通常對語言的理解來說,不同的代碼調用函數,都會擁有一套獨立的局部變量。
            因此下面這段代碼很容易理解:
            //---------------------------------------------------------
            // 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());

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

            變量的局部、全局特性與OOP的封裝性中的“私有(private)”、“公開(public)”具有類同性。因此絕大多數資料總是以下面的方式來說明JavaScript的面向對象系統中的“封裝權限級別”問題:
            //---------------------------------------------------------
            // JavaScript中OOP封裝性
            //---------------------------------------------------------
            function MyObject() {
            ?? // 1. 私有成員和方法
            ?? var private_prop = 0;
            ?? var private_method_1 = function() {
            ???? // ...
            ???? return 1
            ?? }
            ?? function private_method_2() {
            ???? // ...
            ???? return 1
            ?? }

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

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

            // 4. 公開成員和方法(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)”表明只有在(構造)函數內部可訪問,而“特權(privileged)”是特指一種存取“私有域”的“公開(public)”方法。“公開(public)”表明在(構造)函數外可以調用和存取。

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

            然而,從面向對象的角度上來講,上面這些概念都很難自圓其說:JavaScript究竟是為何、以及如何劃分出這些封裝權限和概念來的呢?

            ——因為我們必須注意到下面這個例子所帶來的問題:
            //---------------------------------------------------------
            // 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();

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

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

            在這個測試代碼中,obj1/obj2都是Object()實例。我們使用function.call()的方式來調用setValue/getValue,使得在MyFoo()調用的過程中替換this為obj1/obj2實例。

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

            由此可見,JavaScript在處理局部變量時,對“普通函數”與“構造器”是分別對待的。這種處理策略在一些JavaScript相關的資料中被解釋作“面向對象中的私有域”問題。而事實上,我更愿意從源代碼一級來告訴你真相:這是對象的上下文環境的問題。——只不過從表面看去,“上下文環境”的問題被轉嫁到對象的封裝性問題上了。

            (在閱讀下面的文字之前,)先做一個概念性的說明:
            ?? - 在普通函數中,上下文環境被window對象所持有
            - 在“構造器和對象方法”中,上下文環境被對象實例所持有

            在JavaScript的實現代碼中,每次創建一個對象,解釋器將為對象創建一個上下文環境鏈,用于存放對象在進入“構造器和對象方法”時對function()內部數據的一個備份。JavaScript保證這個對象在以后再進入“構造器和對象方法”內部時,總是持有該上下文環境,和一個與之相關的this對象。由于對象可能有多個方法,且每個方法可能又存在多層嵌套函數,因此這事實上構成了一個上下文環境的樹型鏈表結構。而在構造器和對象方法之外,JavaScript不提供任何訪問(該構造器和對象方法的)上下文環境的方法。

            簡而言之:
            ?? - 上下文環境與對象實例調用“構造器和對象方法”時相關,而與(普通)函數無關
            ?? - 上下文環境記錄一個對象在“構造函數和對象方法”內部的私有數據
            ?? - 上下文環境采用鏈式結構,以記錄多層的嵌套函數中的上下文

            由于上下文環境只與構造函數及其內部的嵌套函數有關,重新閱讀前面的代碼:
            //---------------------------------------------------------
            // 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');

            我們發現setValue()的確可以訪問到位于MyFoo()函數內部的“局部變量i”,但是由于setValue()方法的執有者是MyFoo對象(記住函數也是對象),因此MyFoo對象擁有MyFoo()函數的唯一一份“上下文環境”。

            接下來MyFoo.setValue.call()調用雖然為setValue()傳入了新的this對象,但實際上擁有“上下文環境”的仍舊是MyFoo對象。因此我們看到無論創建多少個obj1/obj2,最終操作的都是同一個私有變量i。

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

            ?? function foo_0() {
            ?? }

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

            window = new Window();
            */

            因此我們可以看到foo_0()與foo_1()能同時訪問global_i和global_j。接下來的推論是,上下文環境決定了變量的“全局”與“私有”。而不是反過來通過變量的私有與全局來討論上下文環境問題。

            更進一步的推論是:JavaScript中的全局變量與函數,本質上是window對象的私有變量與方法。而這個上下文環境塊,位于所有(window對象內部的)對象實例的上下文環境鏈表的頂端,因此都可能訪問到。

            用“上下文環境”的理論,你可以順利地解釋在本小節中,有關變量的“全局/局部”作用域的問題,以及有關對象方法的封裝權限問題。事實上,在實現JavaScript的C源代碼中,這個“上下文環境”被叫做“JSContext”,并作為函數/方法的第一個參數傳入。——如果你有興趣,你可以從源代碼中證實本小節所述的理論。

            另外,《JavaScript權威指南》這本書中第4.7節也講述了這個問題,但被叫做“變量的作用域”。然而重要的是,這本書把問題講反了。——作者試圖用“全局、局部的作用域”,來解釋產生這種現象的“上下文環境”的問題。因此這個小節顯得凌亂而且難以自圓其說。
            午夜不卡久久精品无码免费| 无夜精品久久久久久| 青青草国产97免久久费观看| 久久亚洲高清观看| 久久久久久狠狠丁香| 久久精品视频网| 久久久91精品国产一区二区三区 | 久久精品国产亚洲沈樵| 精品久久久噜噜噜久久久| 久久AV高清无码| a高清免费毛片久久| 青青草原综合久久| 99久久精品这里只有精品| 国产高潮久久免费观看| 久久久久国色AV免费观看| 噜噜噜色噜噜噜久久| 日韩精品久久久久久免费| 久久99国内精品自在现线| 亚洲狠狠综合久久| 日本精品久久久久影院日本 | 久久久久久免费视频| 亚洲精品乱码久久久久久按摩 | 久久国产精品久久久| 91麻精品国产91久久久久| 一级做a爰片久久毛片免费陪| 中文字幕久久精品无码| 欧美激情精品久久久久| 中文字幕精品无码久久久久久3D日动漫 | 久久久久无码国产精品不卡| 久久婷婷国产剧情内射白浆| 99久久99久久精品免费看蜜桃| 国内精品久久久久影院网站| 热99RE久久精品这里都是精品免费| 久久国产精品成人影院| 久久性生大片免费观看性| 99久久免费国产精品热| 欧美日韩精品久久久免费观看| 国内精品久久久久久久97牛牛| 久久亚洲精品国产亚洲老地址| 欧美精品一本久久男人的天堂| 久久久久久国产精品无码下载|