繼承是面向對象編程中很重要的概念,在其它面向對象的語言中大都很簡單,例如java中有關鍵詞extends來實現
javascript語言在ES6也新增了extends關鍵詞可以實現繼承,用法與java其實大同小異:
class Animal { constructor(props) { this.name = props.name } eat(){ console.log(`${this.name} 要吃東西`) } } class Dog extends Animal { constructor(props) { //調用實現父類的構造函數 super(props); this.type = props.type } run(){ console.log(`${this.name}要跑了`) } } const xiaobao = new Dog({name:"xiaobao"}) console.log(xiaobao.eat(),xiaobao.run())
如果不用class 和extends關鍵詞呢?
要實現繼承,那么首先要定義一個被繼承的父類:
function Animal(name){ this.type = "Animal" this.array = [1,2,3] } Animal.prototype.eat = function(type){ console.log(this.name +"喜歡吃"+type) }
1.構造函數
利用call/apply方法改變函數上下文實現繼承,這種辦法有很明顯的缺點:不能繼承父類的原型的屬性或方法
function Dog(name){ Animal.call(this) this.type = "dog" this.name = name } var xb = new Dog("小寶") xb.type // dog xb.array // [1,2,3] xb.eat('骨頭') // Uncaught TypeError: (intermediate value).eat is not a function
2.原型鏈
使子類原型對象指向父類的實例以實現繼承,缺點是會共用原型,不能多繼承,當有多個實例時數據會共通,這當然不是我們想要的。
function Dog(name){ this.type="dog" this.name = name } Dog.prototype = new Animal() var xb = new Dog("小寶") var dd = new Dog("點點") xb.array // [1,2,3] dd.array // [1,2,3] xb.array.push(4) xb.array // [1,2,3,4] dd.array // [1,2,3,4]
3.組合繼承
調用父類構造函數,再使子類原型對象指向父類的實例,缺點是會調用兩次父類構造函數
function Dog(name){ Animal.call(this) this.type="dog" this.name = name } Dog.prototype = new Parent3()
4.組合繼承--優化版
將子類的原型指向父類的原型的對象(將父類的原型用Object.create()處理下,將子父的構造函數隔離開,沒有這一步將會造成父類的構造函數被覆蓋),再修復constructer,將子類構造函數賦給子類的原型的構造函數。
function Dog(name){ Animal.call(this) this.type="dog" this.name = name } Dog.prototype = Animal.prototype Dog.prototype = Object.create(Animal.prototype) Dog.prototype.constructor = Dog
后面兩種方式推薦使用,沒有什么明顯的缺點。