在JavaScript中,每個函數都有一個prototype(原型)屬性,該屬性是一個對象,它的作用是使特定類型的所有對象實例可以共享它所包含的屬性和方法。
原型是JavaScript中非常特殊的一個對象,當一個函數創建之後,會隨之產生一個原型對象,當透過這個函數的構造函數創建了一個具體的對象之後,在這個具體的對像中,就會有一個屬性指向原型。
下面的程式碼示範了透過原型建立JavaScript物件的方式。使用基於原型的建立可以將屬性和方法設定為物件Person專有的的屬性和方法,它們不能再透過window來調用,因此滿足了物件的封裝性的要求。
function Person(){}; Person.prototype.name = "Leon"; Person.prototype.age = 22; Person.prototype.say = fucntion(){ alert(this.name + "," + this.age); } var p1 = new Person(); p1.say();
原型的記憶體模型分析
在使用原型方法建立類別的過程中,原型在記憶體中會有4種不同的狀態。我們仍然以上面創建Person類別的例子來分析原型的記憶體模型。程式碼如下:
// 第一种状态 function Person(){}; // 第二种状态 Person.prototype.name = "Leon"; Person.prototype.age = 22; Person.prototype.say = fucntion(){ alert(this.name + "," + this.age); } // 第三种状态,创建了一个对象之后会有一个_proto_属性指向原型 // 在使用时如果对象内部没有找到该属性,就会去原型中查找 var p1 = new Person(); p1.say(); // 第四种状态 var p2 = new Person(); p2.name = "Ada"; p2.say();
首先,我們透過function Person(){};建立了一個Person函數,此時Person函數的原型記憶體模型如下圖所示:
// 第一种状态 function Person(){};
在記憶體中會為Person函數分配一塊空間,在這個空間中有一個prototype屬性,另外還會為該函數建立一個原型對象,在原型對像中有一個constructor屬性。 Person函數和它的原型物件的關係如圖所示。我們稱這時的記憶體模型為原型的第一種狀態。
接著我們透過原型為Person函數設定屬性和方法,這是原型記憶體模型的第二種狀態。
// 第二种状态 Person.prototype.name = "Leon"; Person.prototype.age = 22; Person.prototype.say = fucntion(){ alert(this.name + "," + this.age); }
加入上面的程式碼後,原型的記憶體模型結構如下圖所示:
此時,透過原型添加的方法和屬性都被儲存在原型的記憶體空間中。
接下來,我們完成Person類別的建立之後,就可以透過new關鍵字來建立Person對象,這是原型記憶體模型的第三種狀態。
// 第三种状态 var p1 = new Person(); p1.say();
原型記憶體模型的第三種狀態如下圖所示:
我們透過new Person()建立了一個Person物件p1,此時會在記憶體中為p1物件分配一塊記憶體空間,在p1的記憶體空間中會有一個_proto_內部屬性,這個內部屬性是不能被存取的,它也指向Person原型。
雖然_proto_內部屬性是隱藏的,但是我們可以透過Person.prototype.isPrototypeOf(p1)方法來偵測p1是否有_proto_指向Person的原型。
console.info(Person.prototype.isPrototypeOf(p1)); // 控制台输出:true
在完成p1物件的創建之後,透過p1物件呼叫了say()方法。此時,在p1物件的記憶空間中是沒有say()方法的。當在p1的記憶體空間中找不到say()方法的時候,JavaScript會透過_proto_屬性到Preson的原型中去查找,找到之後就會執行對應的say()方法。
除了上面的三種狀態之外,原型的記憶體模型還有第四種狀態。
如果我們再建立一個Person物件p2,並且將p2物件的name屬性修改為“Ada”,此時就會出現原型記憶體模型的第四種狀態。
// 第四种状态var p2 = new Person();p2.name = "Ada";p2.say();
調用上面的程式碼之後的原型記憶體模型如下圖所示:
當創建物件p2的時候,同樣會在記憶體中為它分配空間,在p2物件的空間中也會有一個_ proto_內部屬性指向Person的原型。
當我們透過p2.name = "Ada";為物件p2的name屬性賦值的時候,JavaScript會在p2的記憶體空間中設定自己的name屬性,並將值設為「Ada」。
接著我們呼叫了say()方法,該方法中需要取得p2的name屬性,它首先會在p2物件自己的記憶體空間中尋找是不是有name屬性,如果找到了,就不會再去Person原型中取查找了。顯然此時在p2物件的空間中存在一個name屬性,所以呼叫say()方法印出來的名字是“Ada”,而不是“Leon”。
需要特別注意的是:原型中的值是不會被替換的,僅僅只是在屬性查找時被物件自己空間中的同名屬性所覆蓋。
以上就是原型記憶體模型的4種狀態,理解這4種狀態是掌握原型的關鍵所在。
以上就是JavaScript物件導向-原型的記憶體模型的內容,更多相關內容請關注PHP中文網(www.php.cn)!