「繼承」是JavaScript面向對象設計的重要一環,願你認真讀完本文,吃透繼承的概念。app
在js中,繼承主要是依靠原型鏈實現的,原型鏈是通往繼承的必經之路。你能夠參考《一張圖完全KO原型鏈(prototype,__proto__)》這篇博文,相信它必定能很好地幫助你原型鏈。函數
原型鏈是實現繼承最原始的模式,即經過prototype屬性實現繼承。post
//父級-構造函數
function Father() {
this.fatherProp = true
}
//父級-原型屬性
Father.prototype.getFatherValue = function() {
return this.fatherProp
}
//子級-構造函數
function Son() {
this.sonProp = false
}
//子級-原型屬性:繼承父級
//即__proto__指向父級的prototype
//若不理解請閱讀《一張圖完全KO原型鏈(prototype,__proto__》
Son.prototype = new Father()
//子級-添加原型方法
Son.prototype.getSonValue = function() {
return this.sonProp
}
//建立子級的實例對象
var son = new Son()
console.log(son.getFatherValue()) //true
複製代碼
若是到最後都沒找到,會發生什麼呢?學習
//一個不存在的方法
console.log(son.getValue()) //ERROE:not a function
複製代碼
重寫父級原型鏈的方法或者添加父級原型鏈不存在的方法,必須在父級原型鏈代碼以後。(這個很好理解,不放代碼演示了)ui
經過原型鏈實現繼承後,不能再使用字面量的方式建立原型對象,由於會覆蓋原型鏈。this
//子級-原型屬性:繼承父級
Son.prototype = new Father()
//不能像下面這樣,這樣會使得上面的代碼無效
//由於這至關於從新建立了一個原型對象
Son.prototype = {
getSonValue: function() {
return this.sonProp
}
}
複製代碼
世間萬事萬物都不可能十全而十美,原型鏈雖然強大,但也存在缺陷。spa
function Father() {
this.arr = [1,2,3]
}
function Son() {
}
Son.prototype = new Father()
var son1 = new Son()
console.log(son1.arr) //1,2,3
var son2 = new Son()
son2.arr.push(4)
console.log(son2.arr) //1,2,3,4
console.log(son1.arr) //1,2,3,4
複製代碼
方式一中引用類型帶來的問題可借用構造函數的方式解決。其核心思想是:在子級構造函數中調用父級構造函數。prototype
如何實如今一個構造函數中調用另外一個函數?——call()和apply()設計
function Father() {
this.arr = [1,2,3]
}
function Son() {
//call的第一個函數是this指向的對象,即構造函數的實例對象
Father.call(this)
/*上面代碼等同於下面這段代碼: (function() { this.arr = [1,2,3] }).call(this) */
}
var son1 = new Son()
console.log(son1.arr) //1,2,3
var son2 = new Son()
son2.arr.push(4)
console.log(son2.arr) //1,2,3,4
console.log(son1.arr) //1,2,3
複製代碼
//解決傳參問題:
function Father(name) {
this.name = name
}
function Son(name) {
Father.call(this, name)
}
var son1 = new Son("小名")
console.log(son1.name) //小名
var son2 = new Son("一燈")
console.log(son2.name) //一燈
複製代碼
這種方式是經過構造函數實現的,固然也把構造函數自身的問題帶過來了——破壞了複用性。由於每一個實例都建立了一份副本。code
組合繼承 = 原型鏈 + 借用構造函數。取其長避其短:共享的用原型鏈,各自的借用構造函數
function Father(name) {
this.name = name
this.arr = [1,2,3]
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = new Father()
Son.prototype.constructor = Son
Son.prototype.getAge = function() {
console.log(this.age)
}
var son1 = new Son("小名", 23)
son1.arr.push(4)
console.log(son1.arr) //1,2,3,4
son1.getName() //小名
son1.getAge() //23
var son2 = new Son("一燈", 24)
console.log(son2.arr) //1,2,3
son1.getName() //一燈
son1.getAge() //24
複製代碼
Father.call(this, name)——name來自Father
this.age = age; Son.prototype.constructor = Son——age來自Son
Father.prototype.getName——getName方法來自Father.prototype
Son.prototype.getAge——getAge來自Son.prototype
繼續學習繼承的後三種方式