ES5中的繼承
一 原型鏈繼承
原型鏈繼承的原理:直接讓子類的原型對象指向父類實例,當子類實例找不到對應的屬性和方法時,就會往它的原型對象,也就是父類實例上找,從而實現對父類的屬性和方法的繼承。
// 父類
function fat () {
this.name = 'zaq'
}
// 父類上的原型方法
fat.prototype.getName = function() {
return this.name
}
// 子類
function child () {}
// 子類的原型對象方法指向父類 子類中找不到的屬性方法就會向父類找
child.prototype = new fat()
// 不影響繼承 順便綁一下constructor
child.prototype.constructor = child
// child實例就可以訪問父類及其原型上的屬性和方法了
const Child = new child()
Child.name // 'zaq'
Child.getName() // 'zaq'
原型鏈繼承的缺點
1、所有的child實例原型都指向同一個fat實例,因此對child實例修改某個父類引用類型會影響所有的child實例;
2、創建子類時無法向父類傳參,沒有實現super()的功能
function fat () {
this.name = 'zaq'
}
fat.prototype.getName = function() {
return this.name
}
function child () {}
child.prototype = new fat()
child.prototype.constructor = child
//
const Child1 = new child();
const Child2 = new child();
Child1.name = 'lsq'
console.log(Child1.name) // lsq
// 本來父類name屬性是zaq 然後現在==
console.log(Child2.name) // lsq
二 構造函數繼承
構造函數繼承:在子類的構造函數中執行父類的構造函數,併爲其綁定子類的this。讓父類的構造函數把成員的屬性和方法都掛在子類的this上,這樣既避免了共用一個原型實例,又能像父類構造函數傳參。
function fat (name) {
this.name = name
}
fat.prototype.getName = function() {
return this.name
}
function Child () {
fat.call(this, 'zaq')
}
const child1 = new Child()
const child2 = new Child()
child1.name = 'xyc'
console.log(child1.name) // xyc
console.log(child2.name) // zaq
構造函數繼承的缺點
繼承不到父類上的屬性和方法
child1.getName() // Uncaught TypeError: child1.getName is not a function
三 組合式繼承
既然原型鏈繼承和構造函數繼承都有各自的缺點但是又能互補,那何不將兩者結合起來使用;
function fat (name) {
this.name = name
}
fat.prototype.getName = function() {
return this.name
}
// 構造函數繼承
function Child () {
fat.call(this, 'zaq')
}
// 原型鏈繼承
Child.prototype = new fat()
Child.prototype.constructor = Child
const child1 = new Child()
const child2 = new Child()
child1.name = 'lsq'
console.log(child1.name) // lsq
console.log(child2.name) // zaq
child1.getName() // lsq
組合式繼承的缺點
每次創建子類實例都執行了兩次構造函數(fat.call()和new fat()),雖然不影響繼承,但是在子類創建時 原型中會存在兩份相同的屬性和方法。
四 寄生式組合繼承
爲了解決構造函數被執行兩次的問題,我們將指向父類實例 變成轉向父類原型,減少一次構造函數的執行
function fat (name) {
this.name = name
}
fat.prototype.getName = function() {
return this.name
}
// 構造函數繼承
function Child () {
fat.call(this, 'zaq')
}
// 原型鏈繼承
Child.prototype = fat.prototype // 將指向父類實例轉成轉向父類原型
Child.prototype.constructor = Child
const child1 = new Child()
const child2 = new Child()
child1.name = 'lsq'
console.log(child1.name) // lsq
console.log(child2.name) // zaq
child1.getName() // lsq
但是這種方法也存在一個問題,就是子類和父類的原型都指向同一個對象,如果我們對子類原型操作就會對父類原型產生影響。比如給子類Child.prototype新增一個getName方法,那麼父類fat.prototype也增加或是被覆蓋一個getName方法。爲了解決這個問題 我們給fat.prototype做一個淺拷貝;
function fat (name) {
this.name = name
}
fat.prototype.getName = function() {
return this.name
}
// 構造函數繼承
function Child () {
fat.call(this, 'zaq')
}
// 原型鏈繼承
Child.prototype = Object.create(fat.prototype) // 將指向父類實例轉成轉向父類原型
Child.prototype.constructor = Child
const child1 = new Child();
const child2 = new fat();
child1.getName() // zaq
child2.getName() // undefined
到這裏我們就完成了es5裏的繼承,有空更新es6的繼承方法
ES6中的繼承
class介紹 來自阮一峯
es6中的繼承:
1、class可以理解爲functon,由於class本質還是一個function,因此它也有一個prototype屬性。當new一個class時,會把class的protortype屬性賦值給這個新對象的_proto_屬性上;
2、constructor是默認添加的方法,在new一個對象的時候,會自動調用該方法, constructor裏定義自己的屬性;
3、繼承extends和super, class子類名 extends父類名 實現繼承。當然,還得在constructor裏寫上super (父類的參數),意思就是在子類中獲取父類的this指針,相當於fat.call(this),參考
// 父類
class fat {
constructor(props) {
this.name = props || '';
}
getName () {
return this.name
}
}
// 繼承
class Child extends fat {
constructor(props, attrs) { // props繼承父類的屬性,attrs自己私有的屬性
super(props); // 相當於獲取父類的this指向
this.rename = props.rename || ''
this.attrs = attrs
}
// 子類自己的方法
getFatname () {
return this.name
}
getAttrs () {
return this.attrs
}
}
// 通過new實例化一個構造函數
const child1 = new Child({
name: 'zaq',
rename: 'zwj'
}, 'mine attrs')
child1.getName() // zaq
child1.getRename() // zwj
child1.getAttrs() // mine attrs
end… 散會