JS中的原型與原型鏈

JS中的原型能夠類比於Java中的父類。javascript

在Java中實現繼承有接口繼承與實現繼承兩種方式,接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。java

因爲JS中的函數沒有簽名,在ECMAScript沒法實現接口繼承,ECMAScript只支持實現繼承,而其實現繼承的主要依靠原型與原型鏈來實現的。函數

原型模式

咱們建立的每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象(原型對象),而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。以下面例子所示:ui

function Person(){
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person1.sayName();   //"Nicholas"

var person2 = new Person();
person2.sayName();   //"Nicholas"

alert(person1.sayName == person2.sayName);  //true
複製代碼

在上面代碼中,咱們將sayName方法直接添加到Personprototype屬性中,構造函數爲空,此時經過構造函數生成的person1person2兩個實例對象,都可共享Person.prototype的屬性與方法。this

理解原型對象

不管何時,只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象。在默認狀況下,全部原型對象都會自動得到一個constructor(構造)屬性,這個屬性是一個指向prototype屬性所在函數的指針。拿上面的代碼示例來講,Person.prototype.constructor指向Person,經過Person.prototype可繼續爲原型對象添加方法與屬性。spa

建立了自定義的構造函數後,其原型對象默認只會取得constructor屬性,至於其餘方法,都是從Object繼承而來的。當調用構造函數建立一個新實例後,該實例的內部將包含一個指針(內部屬性[[Prototype]]),指向構造函數的原型對象。雖然在腳本中沒有標準方式訪問[[Prototype]],但每一個對象上支持一個屬性__proto__,可經過該屬性訪問原型對象。prototype

須要注意的是,__proto__這個鏈接存在於實例對象與原型對象之間,而不是存在於實例於構造函數之間。3d

之前面使用Person構造函數建立實例對象的代碼爲例,下圖展現了PersonPerson1Person2及原型對象之間的關係。 指針

image

原型關係判斷

對於判斷對象之間是否存在原型關係,有如下三種方式實現。code

__proto__

alert(person1.__proto__ == Person.prototype)  //true
alert(person2.__proto__ == Person.prototype)  //true
複製代碼

因爲person1.__proto__person2.__proto__都指向原型對象,而Person.prototype也指向原型對象,因此返回值都爲true

isPrototypeOf()

alert(Person.prototype.isPrototypeOf(person1)); //true 
alert(Person.prototype.isPrototypeOf(person2)); //true
複製代碼

A.isPrototypeOf(B),判斷A是不是B的原型對象。對於person1.__proto__.isPrototypeOf(person1)的返回值,也是爲true,由於person1.__proto__等於Person.prototype

getPrototypeOf()

alert(Object.getPrototypeOf(person1) == Person.prototype); //true 
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
複製代碼

Object.getPrototypeOf(person1)返回person1的原型對象。

屬性操做

屬性讀取

每當代碼讀取某個對象的屬性時,都會執行一次搜索,目標是給定名字的屬性。搜索首先從實例對象自己開始,若是對象自己存在該屬性,則直接返回該屬性的值,若是沒有找到,則繼續搜索該對象的原型對象,如有就返回屬性值,若是都沒找到,則會返回undefined

前面提到過,原型對象最初只包含constructor屬性,而該屬性也是共享的,所以能夠經過實例對象訪問

alert(person2.constructor)  //function Person(){}
複製代碼

調用person2.constructor時返回function Person(){},證實了原型對象的constructor屬性確實指向構造函數。

屬性修改

雖然能夠經過實例訪問保存在原型中的值,但卻不能經過實例對象重寫原型中的值。若是咱們在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,那咱們就在實例中建立該屬性,該屬性會屏蔽原型中的那個屬性,以下所示。

person1.name = "Greg";
alert(person1.name);   //"Greg" ——來自實例
alert(person2.name);   //"Nicholas" ——來自原型對象
複製代碼

當爲對象添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,雖然原型對象中的同名屬性依舊存在;想要取消屏蔽,可使用delete操做符徹底刪除實例對象中的屬性,而後才能訪問原型中的屬性,以下所示。

person1.name = "Greg";
alert(person1.name);   //"Greg" ——來自實例
alert(person2.name);   //"Nicholas" ——來自原型對象
 
delete person1.name;
alert(person1.name);   //"Nicholas" ——來自原型對象
複製代碼

判斷屬性是否存在hasOwnProperty

此外,使用hasOwnProperty()方法能夠檢測一個屬性是存在於實例中(該方法也是從Object中繼承而來),只有在給定屬性存在於實例對象中時,纔會返回true

alert(person1.hasOwnProperty("name")); //false 
person1.name = "Greg"; 
alert(person1.name); //"Greg"——來自實例
alert(person1.hasOwnProperty("name")); //true 
alert(person2.name); //"Nicholas"——來自原型
alert(person2.hasOwnProperty("name")); //false 
delete person1.name; 
alert(person1.name); //"Nicholas"——來自原型
alert(person1.hasOwnProperty("name")); //false
複製代碼

判斷屬性是否存在in操做符

此外,使用in操做符能夠檢測一個屬性是存在於實例或其原型對象中,存在返回true

alert(person1.hasOwnProperty("name")); //false 
alert("name" in person1); //true
複製代碼

對於屬性name,它存在於person1的原型對象中,但不存在於person1實例中,因此調用hasOwnProperty方法返回false,調用in操做符返回true.

組合使用hasOwnPropertyin操做符可正確判斷屬性是存在於實例中仍是原型對象中。

原型對象的問題

  1. 省略了爲構造函數傳遞初始化參數這一環節,致使全部實例默認狀況下都將取得相同的屬性值。
  2. 原型中的屬性被不少實例共享,這對於函數來講很是合適,對於那些包含基本值得屬性來講也能夠,但對於引用類型來講,問題就比較突出了。
function Person(){
}

Person.prototype = {
    constructor: Person,
    friends : ["Shelby", "Court"],
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Van");

alert(person1.friends);    //"Shelby,Court,Van"
alert(person2.friends);    //"Shelby,Court,Van"
alert(person1.friends === person2.friends);  //true
複製代碼

person1對屬性friends的任何操做,都會立馬反應到person2上。

相關文章
相關標籤/搜索