posts - 310, comments - 6939, trackbacks - 0, articles - 3
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          JavaScript Prototype 詳解(轉(zhuǎn)載)

          Posted on 2009-04-26 16:54 詩(shī)特林 閱讀(5303) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): JavaScript

          本文里講述的是關(guān)于JavaScript的prototype問(wèn)題,至于具體的JavaScript面向?qū)ο蟮木幊探坛蹋?qǐng)各位看客到其他網(wǎng)站搜索一下,或者到這里看看。

          首先開(kāi)始一個(gè)例子,如下:


          1 function A() {
          2     this.t1 = "ffffff";
          3     this.t2 = function (msg) {
          4         alert(msg);
          5     };
          6 };
          7
          8 A.prototype.p1 = "xxxx";
          9
          10 A.prototype.f1 = function () {
          11      do something.
          12 };
          其實(shí)p1,f1是對(duì)function的prototype對(duì)象的操作,大家要明白,function
          也是一個(gè)對(duì)象,對(duì)象也有屬性,而prototype就是function的屬性,該屬性
          也是一個(gè)對(duì)象,不同之處是,function在做為類(lèi)定義的時(shí)候,創(chuàng)建類(lèi)實(shí)例的
          過(guò)程(new的過(guò)程)要參照它的prototype對(duì)象,把prototype對(duì)象的所有
          屬性(也就是Java里的成員,包括成員變量和成員函數(shù))都復(fù)制到新的對(duì)象
          中去,所以可以看出prototype就是模板,而這個(gè)模板是在new一個(gè)對(duì)象之
          前就已經(jīng)存在了。

          上面的JavaScript就好像在定義一個(gè)Java類(lèi),書(shū)寫(xiě)類(lèi)的時(shí)候,除了用不同的聲明
          (Class和Function)區(qū)別,基本沒(méi)有其他的區(qū)別,但在運(yùn)行時(shí)有很大的區(qū)別。
          首先Java要求類(lèi)必須被編譯成字節(jié)碼才能被載入虛擬機(jī),而JavaScript是在運(yùn)行
          代碼的同時(shí),執(zhí)行了類(lèi)似Java的編譯載入的過(guò)程。并且Java的類(lèi)在載入虛擬機(jī)
          后一般就不能再改變類(lèi)的定義了,比如把一個(gè)方法的行為改變或指向另一個(gè)方
          法的引用等。而JavaScript在運(yùn)行期還可以通過(guò)prototype來(lái)改變類(lèi)及所有該類(lèi)生成
          的對(duì)象的行為。例如上面的例子中,在解析完function A的函數(shù)體后,整個(gè)類(lèi)也
          就生成了,這時(shí)候如果new的話(huà)就能得到類(lèi)的實(shí)例,緊接著的代碼又向類(lèi)動(dòng)態(tài)
          添加了新的行為。

          而在function A的函數(shù)體內(nèi)定義的this成員,可以理解為‘后’綁定成員。
          可以這么理解,在new A()的時(shí)候JavaScript建立了一個(gè)臨時(shí)對(duì)象,
          把A.prototype的所有成員復(fù)制到臨時(shí)對(duì)象中,然后再把函數(shù)A中
          定義的this成員也綁定到臨時(shí)對(duì)象中,然后把臨時(shí)對(duì)象返回給用戶(hù)。
          下面是模擬JavaScript的new關(guān)鍵字的處理偽過(guò)程:

          //建立臨時(shí)對(duì)象
          var tobj = {};
          //復(fù)制prototype
          for (var key in A.prototype)
          tobj[key] = A.prototype[key];
          //綁定函數(shù)體內(nèi)的this成員(這個(gè)過(guò)程是JavaScript的內(nèi)部處理,沒(méi)有辦法模擬)
          return tobj to user;

          之所以存在function內(nèi)部定義的this成員,以及prototype的成員是
          有原因的。由于JavaScript的類(lèi)在構(gòu)造時(shí)是可以傳遞構(gòu)造參數(shù)的,
          所以,this成員的行為可能由于參數(shù)的不同而不同。這也就是需要后
          綁定的原因了。在看下一個(gè)例子:

          1 function AA(val1,val2) {
          2     this.test1 = function() {
          3         alert(val1);
          4     };
          5
          6     this.test2 = val2 ? function () { return this.test1;} : function () { return 456; };
          7
          8     this.test3 = val1 ? val1 : function () {alert("no val1");};
          9 }
          這個(gè)例子很好的說(shuō)明了后綁定的實(shí)際使用價(jià)值,所以后綁定對(duì)于成員
          函數(shù)來(lái)說(shuō)是非常有用的,對(duì)于成員變量來(lái)說(shuō)其實(shí)沒(méi)什么實(shí)際用處。
          唯一不同的是,this成員在每次new對(duì)象時(shí)都要被JavaScript引擎解析,
          原因很簡(jiǎn)單,根據(jù)不同的構(gòu)造參數(shù),使它們?cè)谶\(yùn)行期的行為可能有很大
          的不同。而prototype的成員就不會(huì)每次都解析,第一次定義prototype
          成員時(shí)才解析,以后可以直接引用prototype成員,并且更改了prototype
          成員,所有已經(jīng)建立的實(shí)例對(duì)象的相應(yīng)成員都會(huì)被更改。

          在運(yùn)行期可以通過(guò)'對(duì)象名.成員名'來(lái)更改成員,這種方式可以更改this成員
          和prototype成員的默認(rèn)定義,但是更改只限于自身對(duì)象,因?yàn)镴avaScript
          和Java一樣,也是傳值,對(duì)象的引用也是一個(gè)地址值,所以new一個(gè)對(duì)象后,
          prototype的成員也被復(fù)制到那個(gè)對(duì)象上了,再更改那個(gè)對(duì)象的成員,只會(huì)
          影響那個(gè)對(duì)象自身,其他從同一個(gè)類(lèi)new出來(lái)的對(duì)象都不會(huì)有任何變化。

          不能通過(guò)運(yùn)行期設(shè)置'類(lèi).prototype.成員名'來(lái)覆蓋this同名成員,這樣做沒(méi)有
          任何效果。

          通過(guò)復(fù)制一個(gè)對(duì)象的所有屬性到一個(gè)新對(duì)象,是不能通過(guò)修改prototype成員
          來(lái)修改新對(duì)象的成員行為,因?yàn)樾聦?duì)象不是通過(guò)原來(lái)對(duì)象的類(lèi)new出來(lái)的。
          通常的復(fù)制方法如下:

          1 var tobj = {};
          2 for (var key in otherObj)
          3     tobj[key] = otherObj[key];
          看似tobj和otherObj的行為是一致的,他們不是一個(gè)類(lèi)new出來(lái)的。
          一個(gè)很好的辦法可以測(cè)試,比如otherObj是A類(lèi)new出來(lái)的,
          可以通過(guò)使用 (tobj instanceof A) 來(lái)測(cè)試,結(jié)果顯然是false。

          最新的測(cè)試表明,這種復(fù)制方法可以復(fù)制所有自定義方法,
          但是系統(tǒng)提供的默認(rèn)方法是不能被復(fù)制的,即使你顯式的覆蓋了
          系統(tǒng)默認(rèn)提供的方法,如toString方法等。

          最后再談?wù)刾rototype的constructor成員,該成員是對(duì)一個(gè)類(lèi)的構(gòu)造
          函數(shù)的引用,在類(lèi)定義的初期,如果一個(gè)類(lèi)沒(méi)有從其他別的類(lèi)那
          里繼承,該類(lèi)的prototype.constructor屬性保存的是該類(lèi)自身的引用,
          如果該類(lèi)從別的類(lèi)繼承,那么它的constructor屬性就保存了父類(lèi)的
          constructor引用,一般constructor沒(méi)有什么用處,但可以通過(guò)它來(lái)取
          得他的類(lèi)的信息,就像Java里的對(duì)象都有g(shù)etClass()方法,constructor
          就是干這個(gè)用的。有它的好處是,再運(yùn)行期可以改變所有同類(lèi)對(duì)象
          的成員行為,如:

          1 someObj.constructor.prototype.somePrototype = function () {
          2 other process .
          3 }
          因此好的習(xí)慣是在繼承之后把prototype的constructor成員設(shè)置一下,
          否則會(huì)把父類(lèi)的prototype成員改掉,那樣程序的行為就不可預(yù)知了。
          如:

          1 function classA() {
          2
          3 }
          4
          5 classB.prototype = new classA();
          6 classB.prototype.constructor = classB;
          7
          我要說(shuō)的關(guān)于JavaScript的prototype屬性就這么多,大家多提意見(jiàn)多交流。


          評(píng)論

          # re: JavaScript Prototype 詳解(轉(zhuǎn)載)  回復(fù)  更多評(píng)論   

          2011-11-05 18:24 by 范秋海
          有錯(cuò)誤。
          例如:
          var A = function(a){this.a = a};
          A.prototype.b = 0;
          var a1 = new A(1); a1.b = 2;
          var a2 = new A(2);
          alert(a1.a) 輸出1,
          alert(a1.b) 輸出2
          alert(a2.a) 輸出2
          alert(a2.b) 輸出0

          但是如果是這種情況:
          var A = function(a){this.a = a};
          A.prototype.b = {};
          var a1 = new A(1); a1.b.b = 2;
          var a2 = new A(2);
          alert(a1.a) 輸出1,
          alert(a1.b.b) 輸出2
          alert(a2.a) 輸出2
          注意:
          alert(a2.b.b) 輸出2
          主站蜘蛛池模板: 西青区| 恩施市| 陆良县| 定兴县| 麻阳| 墨玉县| 乐亭县| 礼泉县| 西平县| 张北县| 义乌市| 东方市| 迭部县| 玉田县| 锡林郭勒盟| 阿合奇县| 赣州市| 禹州市| 绵竹市| 岳阳市| 思南县| 措美县| 长武县| 湘阴县| 水富县| 孟津县| 上杭县| 阿克陶县| 洛南县| 凤山县| 昌乐县| 时尚| 桃源县| 肃北| 深圳市| 从江县| 盖州市| 泉州市| 日土县| 玉树县| 萝北县|