關注前端小謳,閱讀更多原創技術文章
相關代碼 →javascript
function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() {} SubType.prototype = new SuperType() // SubType的原型 = SuperType的實例,SubType原型被重寫 → SubType 繼承了 SuperType console.log(SubType.prototype.__proto__) // SuperType原型,SuperType實例的[[Prototype]]指向SuperType原型 console.log(SubType.prototype.__proto__.constructor) // SuperType構造函數,SuperType原型的constructor指向SuperType構造函數
var instance = new SubType() console.log(instance.property) // true,SubType繼承了property屬性 console.log(SubType.prototype.hasOwnProperty('property')) // true,property是SuperType的實例屬性,SubType的原型已被重寫爲SuperType的實例 console.log(instance.getSuperValue()) // true,SubType繼承了getSuperValue()方法 console.log(SubType.prototype.hasOwnProperty('getSuperValue')) // false,getSuperValue是SuperType的原型方法,不存在於SubType的實例中 console.log(SuperType.prototype.hasOwnProperty('getSuperValue')) // true
調用子類型構造函數建立實例後,因爲子類型的原型被重寫:前端
[[Prototype]]
指向超類實例(本來指向子類原型)constructor
指向重寫子類原型的構造函數,即超類構造函數(本來指向子類構造函數)console.log(instance.__proto__) // SuperType實例,SubType的原型SubType.prototype已被SuperType的實例重寫 console.log(instance.constructor) // SuperType構造函數,constructor指向重寫原型對象的constructor,即new SuperType()的constructor console.log(instance.constructor === SubType.prototype.constructor) // true,都指向SuperType構造函數
實現了原型鏈後,代碼讀取對象屬性的搜索過程:java
Object
,全部函數的默認原型都是 Object 實例[[Prototype]]
指針,指向Object
的原型即Object.prototype
Object.prototype
上保存着constructor、hasOwnProperty、isPrototypeOf、propertyIsEnumerable、toString、valueOf、toLocaleString
等默認方法,在實例中調用這些方法時,其實調用的是 Object 原型上的方法console.log(SuperType.prototype.__proto__) // {},SuperType的默認原型是Object實例,Object實例的[[Prototype]]指向Object原型 console.log(SuperType.prototype.__proto__ === Object.prototype) // true,都指向Object原型 console.log(SuperType.prototype.__proto__.constructor) // Object構造函數 console.log(Object.keys(SuperType.prototype.__proto__)) // [],Object原型上可枚舉的方法 console.log(Object.getOwnPropertyNames(SuperType.prototype.__proto__)) // [ 'constructor','__defineGetter__','__defineSetter__','hasOwnProperty','__lookupGetter__','__lookupSetter__','isPrototypeOf','propertyIsEnumerable','toString','valueOf','__proto__','toLocaleString' ],Object原型上的全部方法
instanceof
操做符,測試實例與原型鏈中出現過的構造函數instanceof
具體含義:判斷一個構造函數的 prototype 屬性所指向的對象,是否存在於要檢測對象(實例)的原型鏈上console.log(instance instanceof Object) // true,instance是Object的實例 console.log(instance instanceof SuperType) // true,instance是SuperType的實例 console.log(instance instanceof SubType) // true,instance是SubType的實例
isPrototypeOf()
方法,測試實例與原型鏈上的原型isPrototypeOf()
具體含義:判斷一個對象(原型對象)是否存在於要檢測對象(實例)的原型鏈上console.log(Object.prototype.isPrototypeOf(instance)) // true,Object.prototype是instance原型鏈上的原型 console.log(SuperType.prototype.isPrototypeOf(instance)) // true,SuperType.prototype是instance原型鏈上的原型 console.log(SubType.prototype.isPrototypeOf(instance)) // true,SubType.prototype是instance原型鏈上的原型
SubType.prototype.getSubValue = function () { // 給子類原型添加新方法 return false } SubType.prototype.getSuperValue = function () { // 在子類原型中重寫超類原型的方法 return false } var instance2 = new SubType() console.log(instance2.getSubValue()) // false console.log(instance2.getSuperValue()) // false,方法被重寫 var instance3 = new SuperType() console.log(instance3.getSuperValue()) // true,不影響超類型原型中的方法
function SubType2() {} SubType2.prototype = new SuperType() // 繼承 SubType2.prototype = { // 對象字面量重寫原型,繼承關係失效(子類原型被重寫爲Object實例) someFunction: function () { return false }, } var instance4 = new SubType2() console.log(instance4.getSuperValue()) // error,對象字面量重寫了原型,繼承關係已失效
function SuperTypePro(name) { this.nums = [1, 2, 3] // 超類屬性,引用類型 this.name = name // 超類屬性,原始類型 } SuperTypePro.prototype.getSuperNums = function () { return this.nums } function SubTypePro() {} SubTypePro.prototype = new SuperTypePro() // 繼承 var instance5 = new SubTypePro() instance5.nums.push(4) // 在子類實例中,修改(非從新定義)繼承的引用類型屬性 console.log(instance5.nums) // [1,2,3,4] var instance6 = new SubTypePro() console.log(instance6.nums) // [1,2,3,4],超類實例受到影響 var instance7 = new SubTypePro() instance7.nums = [] // 在子類實例中,從新定義(覆蓋)繼承的引用類型屬性 console.log(instance7.nums) // [] console.log(instance6.nums) // [1,2,3,4],超類實例不受影響
var person = new SuperTypePro('Simon') // 建立超類型實例 console.log(person.name) // 'Simon' var person2 = new SubTypePro('Simon') // 建立子類型實例,參數傳遞無心義 console.log(person2.name) // undefined
apply()
或call()
將超類構造函數的做用域綁定給子類的實例 this,再調用超類構造函數function SuperTypeBorrow() { this.nums = [1, 2, 3] } function SubTypeBorrow() { console.log(this) // SubTypeBorrow構造函數內部的this,指向SubTypeBorrow的實例 SuperTypeBorrow.call(this) // 將超類的做用域綁定給this,即子類的實例 } var instance8 = new SubTypeBorrow() console.log(instance8.nums) // [ 1, 2, 3 ] instance8.nums.push(4) console.log(instance8.nums) // [ 1, 2, 3, 4 ] var instance9 = new SubTypeBorrow() console.log(instance9.nums) // [ 1, 2, 3 ],超類不受影響
function SuperTypeParam(name) { this.name = name } function SubTypeParam() { SuperTypeParam.call(this, 'Nicholas') // 繼承,先調用超類型構造函數 this.age = 29 // 再添加子類型中定義的屬性 } var instance10 = new SubTypeParam() console.log(instance10.name, instance10.age) // Nicholas 29
/* 超類型構造函數 */ function SuperTypeMix(name) { this.name = name this.nums = [1, 2, 3] } SuperTypeMix.prototype.sayName = function () { console.log(this.name) } /* 子類型構造函數 */ function SubTypeMix(name, age) { SuperTypeMix.call(this, name) // 盜用構造函數繼承,繼承實例屬性 this.age = age } SubTypeMix.prototype = new SuperTypeMix() // 原型鏈繼承,繼承原型方法 SubTypeMix.prototype.sayAge = function () { console.log(this.age) // 子類型原型添加方法(須在替換原型語句以後) } var instance11 = new SubTypeMix('Nicholas', 29) instance11.nums.push(4) console.log(instance11.nums) // [ 1, 2, 3, 4 ],盜用構造函數繼承而來,屬性保存在超類型實例和子類型原型中 instance11.sayName() // 'Nicholas',原型鏈繼承而來,方法保存在超類型原型中 instance11.sayAge() // 29,非繼承,方法保存在子類型原型中 var instance12 = new SubTypeMix('Greg', 27) console.log(instance12.nums) // [ 1, 2, 3] instance12.sayName() // 'Greg' instance12.sayAge() // 27
組合繼承也有本身的不足,其會調用 2 次超類構造函數git
/* 超類構造函數 */ function SuperTypeMix(name) { this.name = name this.nums = [1, 2, 3] } /* 子類構造函數 */ function SubTypeMix(name) { SuperTypeMix.call(this, name) // 盜用構造函數繼承,繼承屬性(建立子類實例時,第二次調用超類構造函數,子類實例繼承超類實例屬性) } SubTypeMix.prototype = new SuperTypeMix() // 原型鏈繼承,繼承方法(第一次調用超類構造函數,子類原型已經繼承了超類實例和原型中的方法和屬性)
調用 2 次超類型構造函數影響效率,且:github
var instance11 = new SubTypeMix('Nicholas') // 建立子類實例 instance11.nums.push(4) console.log(SubTypeMix.prototype) // SuperTypeMix { name: undefined, nums: [ 1, 2, 3 ], sayAge: [Function] },子類原型(被重寫爲超類實例) console.log(instance11) // SuperTypeMix { name: 'Nicholas', nums: [ 1, 2, 3, 4 ], age: 29 },子類實例 delete instance11.nums // 從子類實例中刪除(繼承自超類實例的)屬性 console.log(instance11) // SuperTypeMix { name: 'Nicholas', age: 29 },子類實例 console.log(instance11.nums) // [ 1, 2, 3 ],仍然能夠(從子類原型中)訪問到該屬性
建立一個函數,接收一個參數對象(必傳)app
function object(o) { function F() {} //函數內部建立臨時構造函數 F.prototype = o // 將傳入的對象做爲這個構造函數的原型 return new F() // 返回這個構造函數的新實例 }
var person = { name: 'Nicholas', } var anotherPerson = object(person) console.log(anotherPerson.name) // 'Nicholas',來自person console.log(anotherPerson.hasOwnProperty('name')) // false anotherPerson.name = 'Greg' // 覆蓋同名屬性 console.log(anotherPerson.hasOwnProperty('name')) // true console.log(anotherPerson.name) // 'Greg',來自副本 console.log(person.name) // 'Nicholas',來自person
ES5 的Object.create()
方法規範化原型式繼承,接收 2 個參數函數
Object.create()
方法與前面提到的object()
函數的行爲相同var anotherPerson2 = Object.create(person) console.log(anotherPerson2.name) // 'Nicholas',來自person
var anotherPerson3 = Object.create(person, { name: { value: 'Greg' }, // 描述符定義對象的屬性,如有同名屬性則覆蓋 }) console.log(anotherPerson3.name) // 'Greg',來自副本
var person2 = { nums: [1, 2, 3], } var anotherPerson4 = Object.create(person2) anotherPerson4.nums.push(4) // 引用類型屬性被修改,非從新定義 console.log(anotherPerson4.nums) // [1, 2, 3, 4],來自person console.log(person2.nums) // [1, 2, 3, 4],做爲原型的引用類型屬性受到影響
與原型式繼承緊密相關,其思路與寄生構造函數和工廠模式相似:測試
function createAnother(original) { var clone = Object.create(original) // 進行原型式繼承,返回一個空實例 console.log(clone) // {},空實例,其原型是orginal對象 clone.sayHi = function () { console.log('Hi') // 給返回的實例對象添加方法(每一個實例從新建立方法) } return clone } var person3 = { name: 'Nicholas', } var anotherPerson5 = createAnother(person3) console.log(anotherPerson5.name) // 'Nicholas' console.log(anotherPerson5.hasOwnProperty('name')) // false,name屬性保存在做爲原型的對象person3上 anotherPerson5.sayHi() // 'Hi' console.log(anotherPerson5.hasOwnProperty('sayHi')) // true,sayHi方法保存在返回的實例對象上 console.log(anotherPerson5) // { sayHi: [Function] }
原型鏈的混成形式:this
// 封裝:原型鏈的混成形式 function inherit(subType, superType) { // 1.建立對象,繼承超類的原型 var superPrototype = Object.create(superType.prototype) // superPrototype的原型是超類原型 console.log(superPrototype.__proto__) // 指向superType.prototype超類原型 console.log(superPrototype.__proto__ === superType.prototype) // true console.log(superPrototype.constructor) // 此時constructor指向超類構造函數 // 2.讓constructor指向子類構造函數 superPrototype.constructor = subType // 3.將建立的對象賦給子類的原型 subType.prototype = superPrototype console.log(subType.prototype.__proto__ === superType.prototype) // true,子類原型繼承超類原型 }
/* 超類 */ function SuperTypeMixParasitic(name) { this.name = name this.nums = [1, 2, 3] } SuperTypeMixParasitic.prototype.sayName = function () { console.log(this.name) } /* 子類 */ function SubTypeMixParasitic(name, age) { SuperTypeMixParasitic.call(this, name) // 盜用構造函數,繼承屬性(只調用1次超類構造函數) this.age = age } inherit(SubTypeMixParasitic, SuperTypeMixParasitic) // 原型鏈的混成形式,繼承方法 SubTypeMixParasitic.sayAge = function () { console.log(this.age) }
寄生組合式繼承是引用類型最理想的繼承範式prototype
var instance13 = new SubTypeMixParasitic('Nicholas', 29) instance13.nums.push(4) console.log(instance13.nums) // [ 1, 2, 3, 4 ],盜用構造函數繼承而來,屬性保存在子類實例([ 1, 2, 3, 4 ])和超類實例([ 1, 2, 3 ])中 console.log(SubTypeMixParasitic.prototype) // SubTypeMixParasitic { constructor: { [Function: SubTypeMixParasitic] sayAge: [Function] } },子類原型不含多餘屬性,只繼承超類原型的方法,且constructor指向子類構造函數
console.log(SubTypeMixParasitic.prototype.constructor) // SubTypeMixParasitic構造函數 console.log(instance13.__proto__ === SubTypeMixParasitic.prototype) // true console.log(SubTypeMixParasitic.prototype.__proto__) // SuperTypeMixParasitic原型 console.log( SubTypeMixParasitic.prototype.__proto__ === SuperTypeMixParasitic.prototype ) // true console.log(SubTypeMixParasitic.prototype.__proto__.constructor) // SuperTypeMixParasitic構造函數 console.log(SubTypeMixParasitic.prototype.__proto__.__proto__) // Object原型 console.log( SubTypeMixParasitic.prototype.__proto__.__proto__ === Object.prototype ) // true console.log(SubTypeMixParasitic.prototype.__proto__.__proto__.constructor) // Object構造函數
instanceof
和isPrototypeOf()
——由於constructor
仍舊指向子類型構造函數console.log(instance13 instanceof SubTypeMixParasitic) // instance13是SubTypeMixParasitic的實例 console.log(instance13 instanceof SuperTypeMixParasitic) // instance13是SuperTypeMixParasitic的實例 console.log(SubTypeMixParasitic.prototype.isPrototypeOf(instance13)) // true,SubTypeMixParasitic.prototype是instance13原型鏈上的原型 console.log(SuperTypeMixParasitic.prototype.isPrototypeOf(instance13)) // true,SuperTypeMixParasitic.prototype13是instance原型鏈上的原型