原型(prototype)是函數特有的屬性。只要創建了一個函數,這個函數就會自動創建一個prototype屬性(顯式原型),並指向該函數的原型對象。原型對象上都有一個constructor屬性,指向prototype屬性所在的函數(即函數本身)。而對於每一個構造函數創建出的實例對象,內部都會有一個[... ...
目錄
原型系統
原型
基本概念
-
原型(prototype)是函數特有的屬性。
-
只要創建了一個函數,這個函數就會自動創建一個prototype屬性(顯式原型),並指向該函數的原型對象。
-
原型對象上都有一個constructor屬性,指向prototype屬性所在的函數(即函數本身)。
-
而對於每一個構造函數創建出的實例對象,內部都會有一個[[Prototype]]屬性(隱式原型),同樣指向函數的原型對象。
-
在Firefox、Safari和Chrome瀏覽器中,每個對象都可以通過__proto__屬性訪問到它們的[[Prototype]]屬性。
-
對其他瀏覽器而言,這個屬性對腳本是不可見的(使用
getPrototypeOf()
方法獲取類的原型)。
-
示例
// 創建一個函數
function Person(){};
// 檢查其是否有prototype這個屬性
console.log(Person.hasOwnProperty('prototype'));
// 查看Person.prototype的內容
console.log(Person.prototype);
從輸出結果可以看出,Person
函數確實擁有prototype
這個屬性,並且Person.prototype
這個原型對象包含了一個constructor
屬性,指向了一個函數,這個函數就是構造函數Person()
。
// 補充上面Person()構造函數的原型
Person.prototype.name = '張三';
Person.prototype.age = 20;
Person.prototype.sayName = function(){
console.log(this.name);
}
// 創建對象
const person = new Person();
// 輸出對象
console.log(person);
從輸出結果可以看出,由於將屬性和方法都放到了prototype
上,輸出person
這個實例對象的時候是找不到name
、age
和sayName()
方法的,只有一個隱式原型對象[[Prototype]]
,在這個隱式原型對象上才能找到name
、age
和sayName()
方法,以及指向構造函數Person()
的constructor
屬性。
使用原型的好處就是可以共用屬性和方法。
// 創建兩個對象
const person1 = new Person();
const person2 = new Person();
console.log(person1 === person2); // false
console.log(person1.sayName === person2.sayName); // true
-
person1 === person2
會返回false
,因為它們是兩個不同的對象。 -
person1.sayName === person2.sayName
會返回true
,因為這兩個不同的對象共用這同一個sayName()
方法。
關係
構造函數、原型對象和實例對象三個的關係如下圖:
改進創建對象的模式
單純使用原型模式創建對象
這種模式將所有的屬性和方法都進行共用。
function Person(){};
// 將屬性添加到原型上
Person.prototype.name = '張三';
Person.prototype.age = 20;
Person.prototype.sayName = function(){
console.log(this.name);
}
const person1 = new Person();
const person2 = new Person();
確實,方法共用後,節省了記憶體,但是缺點是屬性也隨之共用了。
例如上面這個例子,person1
和person2
這兩個對象創建後的name
和age
都是張三
和20
。
可以再手動修改對象的屬性:
person2.name = '李四';
person2.age = 18;
綜合構造函數和原型模式創建對象
將不需要共用的屬性放在構造函數上,將共用的屬性和方法放在原型對象上。
同時,可以使用預設值來改進構造函數。
function Person(name='張三', age=20){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
const person1 = new Person();
const person2 = new Person('李四',18);
console.log(person1);
console.log(person2);
- Person()構造函數的參數列表:
name='張三',age=20
表示name
的預設值是'張三'
,age
的預設值是20
。
另一種設置預設值的方式是:
function Person(name,age){ this.name = name || '張三'; this.age = age || 20; }
如果
name
和age
沒有傳入參數,則為undefined
,在或運算符中會被轉為false
,最終賦值給name
和age
的是或運算符右邊的數據。
-
由於
person1
在創建的時候沒有傳入參數,所以預設是張三,20
;而person2
在創建的時候傳入了參數,所以屬性是李四,18
。 -
輸出兩個對象,發現它們只有
name
和age
兩個私有屬性,sayName()
方法在原型對象上,是共用的。