JS高級(jí)ES6的6種繼承方式
前言:
繼承是面向?qū)ο笾欣仙U劦囊粋€(gè)內(nèi)容,在ECMAScript6
之前,JavaScript
中的繼承可謂是非常的繁瑣的,有各種各樣的繼承,本質(zhì)上所有的繼承都是離不開原型鏈的,ES6
新增的extends
關(guān)鍵字也是通過原型鏈實(shí)現(xiàn)的繼承,但是語法相對(duì)來說就簡(jiǎn)單了很多。
關(guān)于原型鏈的內(nèi)容,可以參考上篇文章兩張圖搞懂原型鏈
本篇文章就來介紹一下在ECMAScript6之前是怎么實(shí)現(xiàn)繼承的。
1.原型鏈繼承
借助于原型鏈繼承本質(zhì)就是修改一下原型的指向即可,實(shí)現(xiàn)代碼如下:
function ParentClass() { this.name = '一碗周' } ParentClass.prototype.getName = function () { return this.name } // 定義子類,將來用于繼承父類 function ChildClass() {} // * 將子類的原型指向父類的實(shí)例化,子類擁有父類實(shí)例化后的內(nèi)容 ChildClass.prototype = new ParentClass() // 將子類進(jìn)行實(shí)例化 var child = new ChildClass() console.log(child.getName()) // 一碗周
上面的代碼圖解如下:
圖中紅色線表示這個(gè)構(gòu)造函數(shù)與實(shí)例對(duì)象的原型鏈,通過這個(gè)原型鏈的關(guān)系,從而實(shí)現(xiàn)了繼承。
這種方式實(shí)現(xiàn)繼承有一個(gè)缺點(diǎn)就是多個(gè)實(shí)例會(huì)導(dǎo)致原型對(duì)象上的內(nèi)容時(shí)共享的,內(nèi)容之間會(huì)互相影響,測(cè)試代碼如下:
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' ]
測(cè)試代碼中的child1
并沒有進(jìn)行修改,但是修改了child1
之后,child1
中的值也發(fā)生了改變。
2.借助構(gòu)造函數(shù)繼承
所謂的借助構(gòu)造函數(shù)繼承(有些資料也稱為偽造對(duì)象或經(jīng)典繼承),就是通過子對(duì)象借助Function.call()或者Function.apply()方法調(diào)用父類構(gòu)造函數(shù)完成繼承,
示例代碼如下所示:
function Parent() { // 父級(jí)對(duì)象 this.parent = 'parent' } Parent.prototype.name = '一碗周' // 為 Parent 父級(jí)對(duì)象的原型增加屬性 function Child() { // 子級(jí)對(duì)象 this.child = 'child' Parent.call(this) // 使用 call() 或者 apply() 方法調(diào)用父級(jí)構(gòu)造函數(shù) 實(shí)現(xiàn)繼承。 } const child = new Child() console.log(child) console.log(child.name) // undefined // 不會(huì)繼承父類的原型
執(zhí)行流程如下所示:
使用這種方式的優(yōu)點(diǎn)是避免了引用類型的實(shí)例被所有對(duì)象共享,缺點(diǎn)是因?yàn)樗械姆椒ǘ级x在了構(gòu)造函數(shù)中,是不會(huì)繼承原型對(duì)象,而且每實(shí)例化一個(gè)對(duì)象之后都會(huì)重新創(chuàng)建一遍這些方法,占用內(nèi)存空間,更別說函數(shù)復(fù)用了。
3.組合式繼承
之前掌握的兩種繼承方式都是存在缺點(diǎn)的,基于原型繼承的繼承方式,所有實(shí)例化后的對(duì)象都共享原型的方法和屬性,如果有一個(gè)更改則都會(huì)進(jìn)行更改。而借助構(gòu)造函數(shù)繼承的方式又無法繼承原型屬性。所以就出現(xiàn)了結(jié)合式繼承,就是將基于原型繼承方式和借助構(gòu)造函數(shù)的繼承方式結(jié)合起來,取其精華去其糟粕的一種繼承方式。
實(shí)現(xiàn)組合式繼承的基本思路如下:
- 使用原型鏈或原型式繼承實(shí)現(xiàn)對(duì)原型的屬性和方法的繼承。
- 通過結(jié)構(gòu)構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例對(duì)象的屬性的繼承。
這樣,既通過在原型上定義方法實(shí)現(xiàn)了函數(shù)的復(fù)用,又可以保證每個(gè)對(duì)象都有自己的專有屬性。
示例代碼如下所示:
// 父級(jí)對(duì)象 function Parent() { this.parent = 'parent' } // 為 Parent 父級(jí)對(duì)象的原型增加屬性 Parent.prototype.name = '一碗周' // 子級(jí)對(duì)象 function Child() { this.child = 'child' // 使用 call() 或者 apply() 方法調(diào)用父級(jí)構(gòu)造函數(shù) 實(shí)現(xiàn)繼承。 Parent.call(this) } // 解決不會(huì)繼承構(gòu)造函數(shù)的原型對(duì)象的問題 Child.prototype = Parent.prototype const child = new Child() console.log(child.name) // 一碗周
4.原型式繼承
我們可以使用Object.create()方法實(shí)現(xiàn)一種繼承,實(shí)例代碼如下:
var person = { name: '一碗周', friends: ['張三', '李四', '王五'], } var anotherPerson = Object.create(person) anotherPerson.name = '一碗甜' anotherPerson.friends.push('趙六') console.log(person.friends) // [ '張三', '李四', '王五', '趙六' ]
該方式的缺點(diǎn)與第一種一樣,都是多個(gè)實(shí)例會(huì)導(dǎo)致原型對(duì)象上的內(nèi)容時(shí)共享的,內(nèi)容之間會(huì)互相影響。
5.寄生式繼承
寄生式繼承的基礎(chǔ)是在原型式繼承的的基礎(chǔ)上,增強(qiáng)對(duì)象,返回構(gòu)造函數(shù),實(shí)例代碼如下:
var person = { name: '一碗周', friends: ['張三', '李四', '王五'], } function createAnother(original) { var clone = Object.create(original) // 通過調(diào)用 object() 函數(shù)創(chuàng)建一個(gè)新對(duì)象 clone.sayMe = function () { // 以某種方式來增強(qiáng)對(duì)象 } return clone // 返回這個(gè)對(duì)象 } var anotherPerson = createAnother(person) anotherPerson.sayMe()
它的缺點(diǎn)與原生式繼承是一樣的。
6.寄生組合式繼承
該繼承方式是借助構(gòu)造函數(shù)傳遞參數(shù)和寄生式繼承所實(shí)現(xiàn)的,實(shí)例代碼如下:
function inheritPrototype(ChildClass, ParentClass) { var prototype = Object.create(ParentClass.prototype) // 創(chuàng)建對(duì)象,創(chuàng)建父類原型的一個(gè)副本 // 修改創(chuàng)建的父類原型副本的 constructor 并將子類的 prototype 指向這個(gè)類,形成與父類無關(guān)聯(lián)的類 prototype.constructor = ChildClass ChildClass.prototype = prototype } // 父類初始化實(shí)例屬性和原型屬性 function ParentClass(name) { this.name = name this.colors = ['red', 'blue', 'green'] } ParentClass.prototype.sayName = function () { console.log(this.name) } // 借用構(gòu)造函數(shù)傳遞增強(qiáng)子類實(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' ]
這個(gè)例子的高效率體現(xiàn)在它只調(diào)用了一次ParentClass
構(gòu)造函數(shù),并且因此避免了在ChildClass.prototype
上創(chuàng)建不必要的、多余的屬性。于此同時(shí),原型鏈還能保持不變;因此,還能夠正常使用instanceof
和isPrototypeOf()。
如果你沒有看懂,那就繼續(xù)看,首先,我們將核心代碼進(jìn)行抽離,如下圖:
上圖中就是我們的核心代碼,然后我們來看一下默認(rèn)的ParentClass
和ChildClass
的原型鏈?zhǔn)鞘裁礃幼拥模?/p>
圖如下:
然后我們調(diào)用inheritPrototype()方法,并將修改ChildClass的原型,解析圖如下:
最后不要忘記了在子類中通過call()
方法調(diào)用父類,從而實(shí)現(xiàn)copy
父類的自有屬性,至此就實(shí)現(xiàn)了一個(gè)比較完善的繼承方式。
結(jié)語:
這篇文章介紹了除extends
關(guān)鍵字之外的六種繼承方式,雖然說在ECMAScript6
中新增了class
關(guān)鍵字以及類相關(guān)的所有內(nèi)容,文本介紹的繼承方式都已經(jīng)不怎么使用了。
但是ECMAScript6
新增的類本質(zhì)上就是語法糖,只要是JavaScript談?wù)摾^承,始終離不開class關(guān)鍵字。
到此這篇關(guān)于JS高級(jí)ES6的6種繼承方式的文章就介紹到這了,更多相關(guān)ES6的6種繼承方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript?ES6語法中l(wèi)et,const?,var?的區(qū)別
這篇文章主要為大家介紹了JavaScript中l(wèi)et,const?,var?的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01JS圖形編輯器場(chǎng)景坐標(biāo)視口坐標(biāo)的相互轉(zhuǎn)換
這篇文章主要為大家介紹了JS圖形編輯器之場(chǎng)景坐標(biāo)視口坐標(biāo)的相互轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01微信小程序 高德地圖SDK詳解及簡(jiǎn)單實(shí)例(源碼下載)
這篇文章主要介紹了微信小程序 高德地圖詳解及簡(jiǎn)單實(shí)例(源碼下載)的相關(guān)資料,需要的朋友可以參考下2017-01-01