原型模式
原型模式在《JavaScript高級程序設(shè)計》中的定義:
我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性若债,這個屬性是一個指針,指向一個對象精绎,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法递宅。如果按照字面意思來理解,那么prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象萝勤。使用這個對象的好處就是可以讓所有對象實例共享它包含的屬性和方法。
這里還有一個__proto__屬性呐伞,每一個對象都擁有這個屬性敌卓,這個屬性指向?qū)?yīng)的構(gòu)造函數(shù)的prototype屬性。
通過原型對象伶氢,我們就不用將信息添加到構(gòu)造函數(shù)中了趟径。
實例對象共享原型對象的屬性
function Person(){}
Person.prototype.name = 'A'
Person.prototype.age = 1
// 實例對象共享原型對象的屬性
let p1 = new Person()
console.log(p1.name, p1.age); // A 1
let p2 = new Person()
console.log(p2.name, p2.age); // A 1
constructor(構(gòu)造函數(shù))屬性
每個原型對象在默認(rèn)情況下都會自動獲取一個constructor(構(gòu)造函數(shù))屬性,這個屬性包含一個指針癣防,指向這個原型所在的函數(shù)蜗巧。在上面的例子中,創(chuàng)建Person函數(shù)后蕾盯,它的prototype指向它的原型對象幕屹,這個原型對象就會獲取獲取constructor屬性,指向Person函數(shù)。
判斷實例對象與原型對象之間的關(guān)系
- Obj.prototype.isPrototypeOf(obj)
console.log(Person.prototype.isPrototypeOf(p1)) // true
- Object.getPrototypeOf(obj):返回對象實例的原型[es5]
console.log(Object.getPrototypeOf(p1) == Person.prototype); // true
console.log(Object.getPrototypeOf(p1).name); // A
- obj.hasOwnPrototype(attr):檢測一個屬性是否存在于實例中望拖。只有當(dāng)實例對象重寫原型對象的屬性后(非原型屬性)才會返回true渺尘。
console.log(p1.hasOwnProperty('name')); // false
p1.name = 'Ertsul'
console.log(p1.hasOwnProperty('name')); //true
in操作符:通過in操作符和hasOwnProperty()可以判斷某屬性存在于實例還是原型對象中【in判斷是否存在屬性,hasOwnProperty()判斷存在于哪里说敏∨父】可以單獨使用和for-in循環(huán)使用:無論屬性屬于實例還是原型中,都會返回true盔沫。(‘name’ in person1)
獲取所有實例的屬性名字锌雀,無論是否可以枚舉。
console.log(Object.getOwnPropertyNames(p1)); // ["name"]
- instanceof
console.log(p1 instanceof Person); // true
更簡單的語法
function Person() { }
Person.prototype = {
name: '001'
}
let p1 = new Person()
console.log(p1.name);
這里的例外是:constructor屬性不再指向Person迅诬;本質(zhì)上相當(dāng)于完全重寫了Person對象了腋逆。注:重寫原型對象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系,它們引用的仍然是最初的原型侈贷。[詳見《JavaScript高級程序設(shè)計》P156-P157惩歉,原型的動態(tài)性]
搜索機制
- 解釋器會現(xiàn)在目標(biāo)對象中搜索目標(biāo)屬性,如果當(dāng)前對象沒有目標(biāo)屬性俏蛮,解釋器繼續(xù)在目標(biāo)對象的原型對象中搜索撑蚌。
- 通過delete只能刪除對象的屬性,而不能刪除原型對象中的屬性搏屑。
組合模式(默認(rèn)類型):構(gòu)造函數(shù)模式 + 原型模式
構(gòu)造函數(shù)模式用于定義實例屬性争涌,而原型模式用于定義方法和共享的屬性。
繼承
原型鏈
// 父親
function Father() {
this.fatherProperty = true
}
Father.prototype.getFatherValue = function () {
return this.fatherProperty
}
// 兒子
function Son() {
this.sonProperty = false
}
// 繼承父親:通過實例化父親辣恋,將實例化對象作為兒子的原型亮垫,這樣兒子就繼承了父親的屬性
Son.prototype = new Father()
Son.prototype.getSonValue = function () {
return this.sonProperty
}
let instance = new Son()
console.log(instance.fatherProperty); // true
上面例子中,通過實例化父親伟骨,將實例化對象作為兒子的原型饮潦,這樣兒子就繼承了父親的屬性。(重寫兒子的原型)携狭。這里继蜡,兒子原型對象的[[prototype]](注:不是prototype,[[prototype]]存在于原型中)指向的是父親的原型對象逛腿,而父親的原型對象的[[prototype]]指向的是Object Prototype稀并。(由于所有函數(shù)的默認(rèn)原型都是Object的實例,所以默認(rèn)原型都會包含一個內(nèi)部指針单默,指向Object.prototype)
借用構(gòu)造函數(shù)
- 主要是為了解決因為引用類型值的原型屬性會被所有實例共享的問題碘举。
- 借用構(gòu)造函數(shù)/偽造對象/經(jīng)典繼承:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
// 父親
function Father() {
this.info = ['Zero']
}
// 兒子
function Son() {
// 繼承父親:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)
// 《JavaScript高級程序設(shè)計》:函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象雕凹,因此通過使用 apply()和 call() 可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)殴俱。
Father.call(this) // 借用超類的構(gòu)造函數(shù)
}
let f = new Father()
f.info.push('100')
console.log(f.info); // ["Zero", "100"]
let s = new Son()
console.log(s.info); // ["Zero"]
如果在繼承的時候要傳遞參數(shù),則在后面添加參數(shù)即可枚抵。如:Father.call(this线欲, param1, param2)
__proto__
通過__proto__也可以實現(xiàn)子類型對超類型的繼承。
const obj = {
num : 123
}
const obj1 = {
__proto__ : obj
}
console.log(obj1.num); // 123
Object.create(source)
const obj = {
num : 123
}
const obj2 = Object.create(obj)
console.log(obj2.num); // 123
Object.assign(Object.create(obj), {…})
const obj = {
num : 123
}
const obj3 = Object.assign(
Object.create(obj),
{
num2 : 234
}
)
console.log(obj3.num, obj3.num2); // 123 234
補充
繼承機制如下圖:
測試代碼如下:
// 父類
function SuperType(){
}
// 子類
function SubType(){
}
// 子類繼承父類
SubType.prototype = new SuperType();
// 實例化父類和子類
let superIns = new SuperType();
let subIns = new SubType();
// 子類
console.log(SubType.prototype); // SuperType {}
console.log(subIns.__proto__); // SuperType {}
// 父類
console.log(SuperType.prototype); // {constructor: ?}
console.log(superIns.__proto__); // {constructor: ?}
console.log(SuperType.prototype.__proto__);
// {constructor: ?, __defineGetter__: ?, __defineSetter__: ?, hasOwnProperty: ?, __lookupGetter__: ?, …}
// 即: Object prototype
console.log(SuperType.prototype.__proto__.constructor);
// ? Object() { [native code] } 即:Object