JS高級ES6的6種繼承方式
前言:
繼承是面向對象中老生常談的一個內容,在ECMAScript6
之前,JavaScript
中的繼承可謂是非常的繁瑣的,有各種各樣的繼承,本質上所有的繼承都是離不開原型鏈的,ES6
新增的extends
關鍵字也是通過原型鏈實現(xiàn)的繼承,但是語法相對來說就簡單了很多。
關于原型鏈的內容,可以參考上篇文章兩張圖搞懂原型鏈
本篇文章就來介紹一下在ECMAScript6之前是怎么實現(xiàn)繼承的。
1.原型鏈繼承
借助于原型鏈繼承本質就是修改一下原型的指向即可,實現(xiàn)代碼如下:
function ParentClass() { this.name = '一碗周' } ParentClass.prototype.getName = function () { return this.name } // 定義子類,將來用于繼承父類 function ChildClass() {} // * 將子類的原型指向父類的實例化,子類擁有父類實例化后的內容 ChildClass.prototype = new ParentClass() // 將子類進行實例化 var child = new ChildClass() console.log(child.getName()) // 一碗周
上面的代碼圖解如下:
圖中紅色線表示這個構造函數(shù)與實例對象的原型鏈,通過這個原型鏈的關系,從而實現(xiàn)了繼承。
這種方式實現(xiàn)繼承有一個缺點就是多個實例會導致原型對象上的內容時共享的,內容之間會互相影響,測試代碼如下:
function ParentClass() { this.colors = ['red', 'blue', 'green'] } function ChildClass() {} ChildClass.prototype = new ParentClass() var child1 = new ChildClass() var child2 = new ChildClass() console.log(child1.colors) // [ 'red', 'blue', 'green' ] child2.colors.push('black') console.log(child2.colors) // [ 'red', 'blue', 'green', 'black' ] console.log(child1.colors) // [ 'red', 'blue', 'green', 'black' ]
測試代碼中的child1
并沒有進行修改,但是修改了child1
之后,child1
中的值也發(fā)生了改變。
2.借助構造函數(shù)繼承
所謂的借助構造函數(shù)繼承(有些資料也稱為偽造對象或經典繼承),就是通過子對象借助Function.call()或者Function.apply()方法調用父類構造函數(shù)完成繼承,
示例代碼如下所示:
function Parent() { // 父級對象 this.parent = 'parent' } Parent.prototype.name = '一碗周' // 為 Parent 父級對象的原型增加屬性 function Child() { // 子級對象 this.child = 'child' Parent.call(this) // 使用 call() 或者 apply() 方法調用父級構造函數(shù) 實現(xiàn)繼承。 } const child = new Child() console.log(child) console.log(child.name) // undefined // 不會繼承父類的原型
執(zhí)行流程如下所示:
使用這種方式的優(yōu)點是避免了引用類型的實例被所有對象共享,缺點是因為所有的方法都定義在了構造函數(shù)中,是不會繼承原型對象,而且每實例化一個對象之后都會重新創(chuàng)建一遍這些方法,占用內存空間,更別說函數(shù)復用了。
3.組合式繼承
之前掌握的兩種繼承方式都是存在缺點的,基于原型繼承的繼承方式,所有實例化后的對象都共享原型的方法和屬性,如果有一個更改則都會進行更改。而借助構造函數(shù)繼承的方式又無法繼承原型屬性。所以就出現(xiàn)了結合式繼承,就是將基于原型繼承方式和借助構造函數(shù)的繼承方式結合起來,取其精華去其糟粕的一種繼承方式。
實現(xiàn)組合式繼承的基本思路如下:
- 使用原型鏈或原型式繼承實現(xiàn)對原型的屬性和方法的繼承。
- 通過結構構造函數(shù)實現(xiàn)對實例對象的屬性的繼承。
這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)的復用,又可以保證每個對象都有自己的專有屬性。
示例代碼如下所示:
// 父級對象 function Parent() { this.parent = 'parent' } // 為 Parent 父級對象的原型增加屬性 Parent.prototype.name = '一碗周' // 子級對象 function Child() { this.child = 'child' // 使用 call() 或者 apply() 方法調用父級構造函數(shù) 實現(xiàn)繼承。 Parent.call(this) } // 解決不會繼承構造函數(shù)的原型對象的問題 Child.prototype = Parent.prototype const child = new Child() console.log(child.name) // 一碗周
4.原型式繼承
我們可以使用Object.create()方法實現(xiàn)一種繼承,實例代碼如下:
var person = { name: '一碗周', friends: ['張三', '李四', '王五'], } var anotherPerson = Object.create(person) anotherPerson.name = '一碗甜' anotherPerson.friends.push('趙六') console.log(person.friends) // [ '張三', '李四', '王五', '趙六' ]
該方式的缺點與第一種一樣,都是多個實例會導致原型對象上的內容時共享的,內容之間會互相影響。
5.寄生式繼承
寄生式繼承的基礎是在原型式繼承的的基礎上,增強對象,返回構造函數(shù),實例代碼如下:
var person = { name: '一碗周', friends: ['張三', '李四', '王五'], } function createAnother(original) { var clone = Object.create(original) // 通過調用 object() 函數(shù)創(chuàng)建一個新對象 clone.sayMe = function () { // 以某種方式來增強對象 } return clone // 返回這個對象 } var anotherPerson = createAnother(person) anotherPerson.sayMe()
它的缺點與原生式繼承是一樣的。
6.寄生組合式繼承
該繼承方式是借助構造函數(shù)傳遞參數(shù)和寄生式繼承所實現(xiàn)的,實例代碼如下:
function inheritPrototype(ChildClass, ParentClass) { var prototype = Object.create(ParentClass.prototype) // 創(chuàng)建對象,創(chuàng)建父類原型的一個副本 // 修改創(chuàng)建的父類原型副本的 constructor 并將子類的 prototype 指向這個類,形成與父類無關聯(lián)的類 prototype.constructor = ChildClass ChildClass.prototype = prototype } // 父類初始化實例屬性和原型屬性 function ParentClass(name) { this.name = name this.colors = ['red', 'blue', 'green'] } ParentClass.prototype.sayName = function () { console.log(this.name) } // 借用構造函數(shù)傳遞增強子類實例屬性(支持傳參和避免篡改) function ChildClass(name, age) { // 拷貝父類所有自有屬性 ParentClass.call(this, name) this.age = age } // 將父類原型指向子類 inheritPrototype(ChildClass, ParentClass) // 新增子類原型屬性 ChildClass.prototype.sayAge = function () { console.log(this.age) } var instance1 = new ChildClass('一碗周', 19) var instance2 = new ChildClass('一碗甜', 18) instance1.colors.push('black') console.log(instance1.colors) // [ 'red', 'blue', 'green', 'black' ] instance1.sayName() // 一碗周 instance2.colors.push('yellow') console.log(instance2.colors) // [ 'red', 'blue', 'green', 'yellow' ]
這個例子的高效率體現(xiàn)在它只調用了一次ParentClass
構造函數(shù),并且因此避免了在ChildClass.prototype
上創(chuàng)建不必要的、多余的屬性。于此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof
和isPrototypeOf()。
如果你沒有看懂,那就繼續(xù)看,首先,我們將核心代碼進行抽離,如下圖:
上圖中就是我們的核心代碼,然后我們來看一下默認的ParentClass
和ChildClass
的原型鏈是什么樣子的,
圖如下:
然后我們調用inheritPrototype()方法,并將修改ChildClass的原型,解析圖如下:
最后不要忘記了在子類中通過call()
方法調用父類,從而實現(xiàn)copy
父類的自有屬性,至此就實現(xiàn)了一個比較完善的繼承方式。
結語:
這篇文章介紹了除extends
關鍵字之外的六種繼承方式,雖然說在ECMAScript6
中新增了class
關鍵字以及類相關的所有內容,文本介紹的繼承方式都已經不怎么使用了。
但是ECMAScript6
新增的類本質上就是語法糖,只要是JavaScript談論繼承,始終離不開class關鍵字。
到此這篇關于JS高級ES6的6種繼承方式的文章就介紹到這了,更多相關ES6的6種繼承方式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript?ES6語法中l(wèi)et,const?,var?的區(qū)別
這篇文章主要為大家介紹了JavaScript中l(wèi)et,const?,var?的區(qū)別,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01