在上一篇文章中我們介紹了原型的記憶體模型,透過4張圖分析了原型在各個階段的狀態。下面我們將首先要介紹一些常用的原型和物件屬性的偵測方法。我們還是以上一篇文章的Person類別為例子,建立Person類別的程式碼如下:
function Person(){}; Person.prototype.name = "Leon"; Person.prototype.age = 22; Person.prototype.say = fucntion(){ alert(this.name + "," + this.age); } var p1 = new Person(); var p2 = new Person(); p2.name = "Ada"; p1.say(); p2.say();
1、偵測某個物件是否是某個函數的原型
alert(Person.prototype.isPrototypeOf(p1)); //true
該方法可以偵測p1的原型是否為Person。
2、偵測某個物件的constructor
alert(p1.constructor == Person); //true
3、偵測某個屬性是否是自己的屬性
alert(p1.hasOwnProperty("name")); //false alert(p2.hasOwnProperty("name")); //true
物件p1在自己的空間中沒有name屬性,所以回傳false。而物件p2重新為name屬性進行了賦值,在它的空間中存在name屬性,所以回傳true。
4、透過delete刪除自己空間中的屬性
delete p2.name; p2.say(); alert(p2.hasOwnProperty("name")); //false
我們可以使用delete來刪除物件自己空間中的某個屬性,如上面程式碼所示。
5、透過in屬性偵測某個物件在原型或自己中是否包含有某個屬性
alert("name" in p1); //在原型中有,所以为true alert("name" in p2); //在自己的空间中有,所以为true
如果某個屬性在原型和自己的空間中都沒有,得到的結果就是false。
6、自訂方法檢測某個屬性是否在原型中存在
function hasPrototypeProperty(obj,prop){ return(!obj.hasOwnProperty(prop) && (prop in obj)); }
上面程式碼中我們自訂了一個偵測某個屬性是否在原型中存在的方法,可以如下使用這個方法:
alert(hasPrototypeProperty(p1,"name")); //true alert(hasPrototypeProperty(p2,"name")); //false
因為p1物件的name屬性是存在於原型中的,所以回傳true,而p2物件的name屬性是在自己空間中的,所以回傳false。
重寫原型
如果我們像前面那樣寫程式碼,會有大量的Person.prototype.xxx語句,這樣不便於我們閱讀和理解。我們可以透過類似Json格式來重寫原型,程式碼如下:
//重写原型 Person.prototype = { name:"Leon", age:22, say:function(){ alert(this.name+ "," + this.age); } } var p1 = new Person(); p1.say();
在使用上面的方法對原型進行重寫之後,由於原型重寫了,而且沒有透過Person.prototype來指定,此時的constructor不會再指向Person,而是指向了Object。
alert(p1.constructor == Person); //false
如果constructor對於你的程式來說真的比較重要,可以在json中聲明原型的指向。
Person.prototype = { constructor:Person, //手动指定constructor name:"Leon", age:22, say:function(){ alert(this.name+ "," + this.age); } }
原型重寫的問題
在對原型進行重寫的時候,我們可能會遇到下面的一些問題。下面我們先來看一段會出現問題的原型重寫的程式碼,然後再對程式碼中每個階段進行記憶體模型分析。程式碼如下:
// 创建Person类 function Person(){} var p1 = new Person(); //在Person的原型上添加了sayHi()方法 Person.prototype.sayHi = function(){ alert(this.name + "Hi!"); } p1.sayHi(); //输出: undefined:hi! // 对Person原型进行重写 Person.prototype = { constructor:Person, //手动指定constructor name:"Leon", age:22, say:function(){ alert(this.name+ "," + this.age); } } var p2 = new Person(); p2.sayHi(); p2.say();//正确 p1.say();//报错
在上面的程式碼中,我們首先建立了一個Person類,此時Person原型的記憶體模型如下圖所示:
接著我們創建了p1對象,然後在Person的原型中新增了一個sayHi()方法。此時Person原型的記憶體模型如下圖所示:
var p1 = new Person(); //在Person的原型上添加了sayHi()方法 Person.prototype.sayHi = function(){ alert(this.name + "Hi!"); } p1.sayHi(); //输出: undefined:hi!
注意現在的記憶體模型,由於在這種狀態中,在p1物件自己的空間和Person原型中都沒有name屬性,所以執行p1.sayHi()方法的時候,this.name屬性為undefined,最終的輸出結果為undefined:hi!。
再接下來我們對Person原型進行了重寫,在原型中加入了一些屬性和方法。
// 对Person原型进行重写 Person.prototype = { constructor:Person //手动指定constructor name:"Leon", age:22, say:function(){ alert(this.name+ "," + this.age); } } p1.sayHi(); //输出: undefined:hi!
此時的原型內存模型如下圖所示:
原型重寫之後,JavaScript會為原型分配一塊新的內存,Person類指向新的原型對象,而原來創建的p1對象的_ proto_屬性仍然指向前面的原型物件。
這時候,如果我們在執行p1.sayHi(),程式不會報錯,但是執行的結果仍然是undefined:hi!,因為在p1物件和它所指向的原型中都沒有name屬性。
最後,在原型重寫之後,我們建立了物件p2。
var p2 = new Person(); p2.sayHi();
此時的原型記憶體模型如下圖所示:
新建立的p2物件的_proto_屬性指向的是重寫後的原型對象,如果此時執行p2.sayHi()方法,那麼在p2物件和它所指向的原型上都沒有sayHi()方法,所以程式會報錯。
如果此時執行p1.say()方法,由於在p1和它指向的原型中都沒有say()方法,所以程式會報錯。
透過上面的原型記憶體模型的分析,我們可以知道原型重寫位置會直接影響物件的屬性和方法,在重寫前創建的物件和在重寫後創建的物件擁有的屬性和方法是不一樣的。上面的幾張記憶體模型圖一定要時時謹記在腦海中。
以上就是JavaScript物件導向-原型的重寫的內容,更多相關內容請關注PHP中文網(www.php.cn)!