讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議80:合理使用原型 >

建議80:合理使用原型

1.使用原型設置默認值

JavaScript是一種動態語言,它的對象系統也是動態的。在程序中,可以根據需要設置原型值,從而影響所有實例對象。如果為構造函數定義了與原型屬性同名的本地屬性,則本地屬性會覆蓋原型屬性,例如:


function p(x){//構造函數

this.x=x;

}

p.prototype.x=1;

var p1=new p(10);

alert(p1.x);//10,本地屬性覆蓋原型屬性


但原型屬性並沒有被刪除,它依然存在,僅是被同名的本地屬性覆蓋了,如果使用delete運算符刪除本地屬性,那麼原型屬性依然會顯示出來。因此,當原型屬性與本地屬性同時存在時,它們之間可以出現交流現象。利用這種現象為對像初始化默認值,例如:


function p(x){//構造函數

if(x)//如果參數存在,則使用該參數設置屬性,此條件是關鍵

this.x=x;

}

p.prototype.x=0;//利用原型屬性,設置本地屬性x的默認值

var p1=new p;

alert(p1.x);//0,顯示本地屬性的默認值

var p2=new p(1);

alert(p2.x);//1,顯示本地屬性的初始化值


2.使用原型實現數據備份

把本地對象的數據完全賦值給原型對象,相當於為該對像定義一個副本,這就是備份對象。當對像屬性被修改時,可以通過原型對像來恢復本地對象的初始值。下面示例演示了如何使用原型備份數據。


function p(x){//構造函數

this.x=x;

}

p.prototype.backup=function{//原型方法,備份本地對象的數據到原型對像中

for(var i in this){

p.prototype[i]=this[i];

}

}

var p1=new p(1);

p1.backup;//備份實例對像中的數據

p1.x=10;//改寫本地對象的屬性值

alert(p1.x);//10,說明屬性值已經被改寫

p1=p.prototype;;//恢復備份

alert(p1.x);//1,說明對象的屬性值已經被恢復到原始值


3.使用原型設置只讀屬性

利用原型還可以為對像屬性設置「只讀」特性,這樣可以避免對像內部數據被任意篡改。這裡的「只讀」只是一個表象,並不是真正禁止對像屬性進行修改,而是借助閉包體存儲屬性值,這樣就可以避免屬性值被動態修改。

下面示例演示了如何根據平面上兩點坐標來計算它們之間的距離。構造函數p用來設置定位點坐標,如果傳遞了兩個參數值,會返回以參數為坐標值的點;如果省略參數,則默認點為原點(0,0)。而在構造函數l中,通過傳遞的兩點坐標對像來計算它們的距離。

如果無意間修改了構造函數的方法b或e的值,則構造函數中length方法的計算值也隨之發生變化。這種動態效果對於動態跟蹤兩點坐標變化來說,是非常必要的。但是,我們並不需要在初始化實例之後隨意地改動坐標值,畢竟方法b和f與參數a和b是沒有多大聯繫的,但它們的參數值卻同時指向同一個對象的引用指針。


function p(x,y){//求坐標點構造函數

if(x)this.x=x;//初始x軸值

if(y)this.y=y;//初始y軸值

p.prototype.x=0;//默認x軸值

p.prototype.y=0;//默認y軸值

}

function l(a,b){//求兩點間距離的構造函數

var a=a;

var b=b;

var w=function{//計算x軸距離,返回對函數引用

return Math.abs(a.x-b.x);

}

var h=function{//計算y軸距離,返回對函數引用

return Math.abs(a.y-b.y);

}

this.length=function{//計算兩點間距離,使用小括號調用私有方法w和h

return Math.sqrt(w*w+h*h);

}

this.b=function{//獲取起點坐標對像

return a;

}

this.e=function{//獲取終點坐標對像

return b;

}

}

var p1=new p(1,2);//聲明一個點

var p2=new p(10,20);//聲明另一個點

var l1=new l(p1,p2);//實例化構造函數,傳遞兩點對像

alert(l1.length);//20.12461179749811,調用length方法計算兩點間距離

l1.b.x=50;//不經意地改動方法b的一個屬性為50

alert(l1.length);//43.86342439892262,說明上面改動影響到兩點間距離


為了避免因為改動方法b的屬性x的值而影響兩點間距離,可以在方法b和e中新建一個臨時性的構造類,設置該類的原型為a,然後實例化構造類並返回,這樣就阻斷了方法b與私有變量a的直接聯繫,它們之間僅是值的傳遞,而不是對對像a的引用,從而避免因方法b的屬性值變化而影響私有對象a的屬性值。


this.b=function{//方法b

function temp{};//臨時構造類

temp.prototype=a;//把私有對象傳遞給臨時構造類的原型對像

return new temp;//實例化後的對象,阻斷直接返回a所出現的引用關係

}

this.e=function{//方法f

function temp{};//臨時構造類

temp.prototype=a;//把私有對象傳遞給臨時構造類的原型對像

return new temp;//實例化後的對象,阻斷直接返回a所出現的引用關係

}


還有一種方法,這種方法是在為私有變量w和h賦值時,不是向函數賦值,而是函數調用表達式,這樣私有變量w和h存儲的是值類型數據,而不是對函數結構的引用,從而不再受後期相關屬性值的影響。


function l(a,b){//求兩點間距離的構造函數

var a=a;

var b=b;

var w=function{//計算x軸距離,返回函數表達式的計算值

return Math.abs(a.x-b.x);

}

var h=function{//計算y軸距離,返回函數表達式的計算值

return Math.abs(a.y-b.y);

}

this.length=function{//計算兩點間距離,直接使用私有變量w和h來計算

return Math.sqrt(w*w+h*h);

}

this.b=function{//獲取起點坐標對像

return a;

}

this.e=function{//獲取終點坐標對像

return b;

}

}


4.使用原型進行批量複製

先看下面一個示例:


function f(x){//構造函數

this.x=x;

}

var a=;

for(var i=0;i<100;i++){//使用for循環結構批量複製構造類f的同一個實例

a[i]=new f(10);//把實例分別存入數組

}


上面的代碼演示了如何複製同一個實例對像100次。如果後期需要修改數組中每個實例對象,就會非常麻煩。現在可以嘗試使用原型來進行批量複製操作,如下所示。


function f(x){//構造函數

this.x=x;

}

var a=;

function temp{};//定義一個臨時的空構造類temp

temp.prototype=new f(10);//把構造類f實例化,並把該實例傳遞給構造類temp的原型對像

for(var i=0;i<100;i++){;//使用for循環結構批量複製臨時構造類temp的同一個實例

a[i]=new temp;//把實例分別存入數組

}


把構造類f的實例存儲在臨時構造類的原型對像中,然後通過臨時構造類temp實例來傳遞複製的值。因此,要想修改數組的值,只需要修改類f的原型即可,從而避免逐一修改數組中每個元素。