原型模式
紅寶書147頁 (以下例項均摘抄自紅寶書,不全,例項可直接翻書)
- 一圖說明一切:
- 所有的建構函式都會有一個prototype屬性(無需定義),指向該建構函式的原型物件(prototype是指標不是物件!)
- 所有原型物件都會有一個constructor屬性(無需定義),指向其對應的建構函式(constructor是指標不是物件!)
- 建構函式生成的例項都會有一個_proto_屬性(無需定義,對開發者不可見),指向建構函式的原型物件
相關函式
- 無法訪問_proto_,但可以通過isPrototypeOf()確定物件與原型物件的關係:
Person.prototype.isPrototypeOf(person1) //true
複製程式碼
- Object.getPrototypeOf(),可以返回_proto_的值:
Object.getPrototypeOf(person1) == Person.prototype //true
複製程式碼
同名覆蓋
-
當為物件例項新增一個屬性時,這個屬性就會遮蔽原型物件中儲存的同名屬性,但不會修改那個屬性。使用delete操作符可以完全刪除例項屬性,但不能刪除原型屬性及變數
-
hasOwnProperty()可以檢測一個屬性是存在於例項還是存在於原型。只有當給定屬性存在於物件例項中,才會返回true:
person1.hasOwnProperty("name")
複製程式碼
- Object.getOwnPropertyDescriptor() 用於獲取例項屬性的描述符,如果要取得原型屬性的描述符,必須在原型物件上呼叫Object.getOwnPropertyDescriptor(該例項摘自MDN,無原型物件的例子):
var o, d;
o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
o = { bar: 42 };
d = Object.getOwnPropertyDescriptor(o, "bar");
// d {
// configurable: true,
// enumerable: true,
// value: 42,
// writable: true
// }
o = {};
Object.defineProperty(o, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
d = Object.getOwnPropertyDescriptor(o, "baz");
// d {
// value: 8675309,
// writable: false,
// enumerable: false,
// configurable: false
// }
複製程式碼
原型與in操作符
- in操作符的使用:單獨使用和for-in迴圈中使用
- 單獨使用:in操作符會在通過物件能夠訪問給定屬性時返回true,無論該屬性存在於例項中還是原型中
function Person(){}
Person.prototype.name = "li"
var person1 = new Person();
alert("name" in person1) //true
複製程式碼
- object.hasOwnProperty()可判斷屬性是否在例項上,與in結合使用,可以寫出判斷屬性是否在原型上的函式:
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
複製程式碼
- for-in迴圈,返回的是所有能夠通過物件訪問的、可列舉的(enumerated)屬性,其中既包括存在於例項中的屬性,也包括存在於原型中的屬性。如果有例項屬性遮蔽了原型屬性,且該原型屬性為不可列舉的,該實力屬性也會在for-in迴圈中返回。
-
正常情況下,開發人員直接定義的屬性都是可列舉的
-
要取得物件上所有可列舉的例項屬性,可以使用Object.keys()方法,其返回一個包含所有可列舉屬性的字串陣列
- 如果想要得到所有例項屬性,無論它是否可列舉,都可以使用Object.getOwnPropertyNames()方法
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys);//"constructor,name,age"
複製程式碼
更簡單的原型語法
- 直接用物件字面量形式建立新物件:
Person.prototype = {
name:"li",
age: 29
}
複製程式碼
- 但是這樣等同於完全重寫了prototype物件,因此constructor屬性也就變成了新物件的constrctir屬性(指向Object建構函式),不再指向Person函式。所以,可以專門寫個constructor:
Person.prototype = {
constructor : Person,
...
}
複製程式碼
- 這種方法重設的constructor屬性會導致其Enumerable特性被設定為true,預設情況下,原生的constructor屬性是不可列舉的。可以使用Object.defineProperty():
Object.defineProperty(Person.prototype, "constructor", {
enumrable:false,
value:Person
})
複製程式碼
原型的動態性
- 正常情況下,給原型定義屬性或方法,會在例項上實時展現;但是如果重寫了原型,例項中的指標不會指向重寫的原型
function Person(){}
var friend = new Person();
Person.prototype = {
constructor:Person,
name:"li"
}
friend.name //undefined
複製程式碼