本篇博客主要來整合一下JS繼承的六種方式及其優缺點。首先咱們提供一個父類:javascript
// 父類 function Person(name) { this.name = name; this.showName = function () { console.log(this.name); } }
本節中咱們使用到的子類以下:java
// 子類 function Staff(office) { this.office = office; this.showOffice = function(){ console.log(this.office); } }
Staff.prototype = new Person('staff'); const a = new Staff('D302'); const b = new Staff('D232'); a.showName(); // satff a.showOffice(); // D302 b.showName(); // staff b.showOffice(); // D232
上述語句即爲原型鏈繼承,此時,a
與b
有一個共有屬性name
,要注意此時name爲基本數據類型,而原型鏈繼承構造的子對象之間共享的是引用數據類型,若是我單獨對a
的name
屬性進行修改,那麼b
的name
屬性不會所以而改變。舉例說明以下:函數
Staff.prototype = new Person('staff'); const a = new Staff('D302'); const b = new Staff('D232'); a.name = 'change' a.showName(); // change b.showName(); // staff
可是,若是Person
對象的name
屬性值引用類型的話,那麼構造出的子對象的屬性就有可能會受到影響,實例以下:this
Staff.prototype = new Person([1,2,3]); const a = new Staff('D302'); const b = new Staff('D232'); a.showName(); // [1,2,3] b.showName(); // [1,2,3] a.name.push(4); a.showName(); // [1,2,3,4] b.showName(); // [1,2,3,4] a.name = [1,2] a.showName(); // [1,2] b.showName(); // [1,2,3,4]
你們能夠看到如上兩種狀況,我在往name
屬性裏面push的時候a
和b
的屬性值都發生了變化,可是我在將a
的name
屬性從新賦值爲[1,2]時,b
的name
值並無發生變化。這是由於進行push
操做以後引用數據類型的地址值並無發生變化,而b
的name
屬性指向的地址與a
的name
屬性指向的地址相同,因此a
和b
會同時發生變化。可是我將其從新複製時,a
的name
屬性指向的地址發生了變化,b指向的仍是原來的地址,因此a
和b
並無發生同步變化。prototype
優勢: 能經過instanceOf
和isPrototypeOf
的檢測code
缺點:對象
父對象中的屬性也變成了子對象的prototype中的公用屬性繼承
2. 不能向父類型的構造函數中傳遞參數 3. 只能繼承一個父類
function Staff(name, office) { Person.call(this, name); this.office = office; this.showOffice = function(){ console.log(this.office); } } const a = new Staff('a', 'D302'); const b = new Staff('b', 'D232'); a.showName(); // a b.showName(); // b console.log(a instanceof Person); // false
經過這個實例咱們能夠看出咱們能夠向父類傳遞參數了,而且能夠繼承多個父類函數,只要調用屢次call
函數便可.ip
優勢:內存
缺點:沒法實現對構造函數的複用
function Staff(name, office) { Person.call(this, name); this.office = office; this.showOffice = function(){ console.log(this.office); } } Staff.prototype = new Person(); const a = new Staff('a', 'D302'); const b = new Staff('b', 'D232'); a.showName(); // a b.showName(); // b console.log(a instanceof Person); // true
這種方法就是對原型鏈繼承和構造函數繼承的結合。
優勢:
缺點:調用了兩次父類構造函數,更耗內存
function container(obj){ function F() {} F.prototype = obj; return new F(); } const child = new Person('child'); const staff = container(child); staff.showName(); // child
這種繼承方式其實能夠看作是直接複製了父類。
缺點:沒法複用
function container(obj){ function F() {} F.prototype = obj; return new F(); } function Staff(obj, office) { const sub = container(obj); sub.office = office; sub.showOffice = function(){ console.log(this.office); } return sub; } const child = new Person('child'); const a = Staff(child, 'D232'); const b = Staff(child, 'C433'); a.showName(); // child a.showOffice(); // D232 b.showName(); // child b.showOffice(); // C433
這種繼承方式就是在原型式繼承的基礎上又加了一個函數用來新增長屬性,解決了原型式繼承須要後續增長屬性的問題。
寄生組合式繼承強化的部分就是在組合繼承的基礎上減小一次多餘的調用父類的構造函數:
function Staff(name, office) { Person.call(this, name); this.office = office; this.showOffice = function () { console.log(this.office); } } Staff.prototype = Object.create(Person.prototype); Staff.prototype.constructor = Person; const a = new Staff('a', 'D232'); const b = new Staff('b', 'C433'); a.showName(); // a a.showOffice(); // D232 b.showName(); // b b.showOffice(); // C433