es5/es6 繼承總結

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… 散會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章