JS中常見的8種繼承方法總結(jié)
1. 原型鏈繼承:
原型鏈繼承是JavaScript中最基本的繼承方式。每個對象都有一個原型對象,通過原型鏈將屬性和方法沿著對象鏈傳遞下來。在原型鏈繼承中,通過將子構(gòu)造函數(shù)的原型對象指向父構(gòu)造函數(shù)的實例,實現(xiàn)了繼承。這意味著子對象可以訪問父對象原型鏈上的屬性和方法。
function Parent() { this.name = 'Parent'; } Parent.prototype.sayHello = function() { console.log('Hello'); }; function Child() { this.name = 'Child'; } Child.prototype = new Parent(); var child = new Child(); child.sayHello(); // Hello function Parent() { this.name = 'Parent'; } Parent.prototype.sayHello = function() { console.log('Hello'); }; function Child() { this.name = 'Child'; } Child.prototype = new Parent(); var child = new Child(); child.sayHello(); // Hello
在這個例子中,Child對象的原型對象被設(shè)置為Parent的一個實例。這樣,當(dāng)我們調(diào)用child.sayHello()時,它會首先在子對象上查找sayHello方法,然后在父對象原型鏈上找到并執(zhí)行該方法。注意:原型鏈繼承的缺點是,所有子對象共享同一個原型對象,對原型對象的修改會影響到所有子對象。
2. 構(gòu)造函數(shù)繼承(經(jīng)典繼承):
構(gòu)造函數(shù)繼承是通過在子構(gòu)造函數(shù)中調(diào)用父構(gòu)造函數(shù)來實現(xiàn)繼承。在構(gòu)造函數(shù)繼承中,通過在子構(gòu)造函數(shù)中使用**call()或apply()**方法,將父構(gòu)造函數(shù)的上下文設(shè)置為子對象的上下文,從而實現(xiàn)繼承。
function Parent(name) { this.name = name; } function Child(name) { Parent.call(this, name); } var child = new Child('Child'); console.log(child.name); // Child function Parent(name) { this.name = name; } function Child(name) { Parent.call(this, name); } var child = new Child('Child'); console.log(child.name); // Child
在這個例子中,我們通過在Child構(gòu)造函數(shù)內(nèi)部調(diào)用Parent構(gòu)造函數(shù),并傳遞子對象特定的參數(shù),實現(xiàn)了繼承。注意:構(gòu)造函數(shù)繼承只能繼承父構(gòu)造函數(shù)的實例屬性,無法繼承父構(gòu)造函數(shù)的原型對象上的方法。
3. 組合繼承:
組合繼承結(jié)合了原型鏈繼承和構(gòu)造函數(shù)繼承,既繼承了父構(gòu)造函數(shù)的屬性,又繼承了父構(gòu)造函數(shù)原型對象上的方法。在組合繼承中,通過調(diào)用父構(gòu)造函數(shù)的方式實現(xiàn)屬性的繼承,通過將子構(gòu)造函數(shù)的原型對象指向父構(gòu)造函數(shù)的實例實現(xiàn)方法的繼承。
function Parent(name) { this.name = name; } Parent.prototype.sayHello = function() { console.log('Hello'); }; function Child(name) { Parent.call(this, name); } Child.prototype = new Parent(); var child = new Child('Child'); child.sayHello(); // Hello function Parent(name) { this.name = name; } Parent.prototype.sayHello = function() { console.log('Hello'); }; function Child(name) { Parent.call(this, name); } Child.prototype = new Parent(); var child = new Child('Child'); child.sayHello(); // Hello
在這個例子中,我們通過調(diào)用**Parent.call(this, name)來繼承父構(gòu)造函數(shù)的屬性,并通過Child.prototype = new Parent()**將子構(gòu)造函數(shù)的原型對象指向父構(gòu)造函數(shù)的實例,從而實現(xiàn)方法的繼承。組合繼承的優(yōu)點是既能夠繼承父構(gòu)造函數(shù)的屬性,又能夠繼承父構(gòu)造函數(shù)原型對象上的方法。然而,缺點是在創(chuàng)建子對象時會調(diào)用兩次父構(gòu)造函數(shù),一次是在設(shè)置原型時,一次是在創(chuàng)建子對象時。這樣會產(chǎn)生一些不必要的開銷。
4. 原型式繼承:
原型式繼承是通過使用一個臨時構(gòu)造函數(shù)和**Object.create()**方法來實現(xiàn)繼承。
var parent = { name: 'Parent', sayHello: function() { console.log('Hello'); } }; var child = Object.create(parent); console.log(child.name); // Parent child.sayHello(); // Hello var parent = { name: 'Parent', sayHello: function() { console.log('Hello'); } }; var child = Object.create(parent); console.log(child.name); // Parent child.sayHello(); // Hello
在這個例子中,我們創(chuàng)建了一個parent對象,然后使用**Object.create()**方法創(chuàng)建了一個新對象child,并將其原型對象指向parent對象,實現(xiàn)了繼承。原型式繼承的本質(zhì)是創(chuàng)建一個新對象,將其原型對象指向另一個已有的對象。這種方式可以實現(xiàn)屬性和方法的繼承,但是不能傳遞構(gòu)造函數(shù)的參數(shù)。
5. Object.create()
Object.create() 是把現(xiàn)有對象的屬性,掛到新建對象的原型上,新建對象為空對象
ECMAScript 5通過增加Object.create()方法將原型式繼承的概念規(guī)范化了。這個方法接收兩個參數(shù):作為新對象原型的對象,以及給新對象定義額外屬性的對象(第二個可選)。在只有一個參數(shù)時,Object.create()與這里的函數(shù)A方法效果相同。
let person = { name: 'mjy', age: 19, hoby: ['唱', '跳'], showName() { console.log('my name is: ', this.name) } } let child1 = Object.create(person) child1.name = 'xxt' child1.hoby.push('rap') let child2 = Object.create(person) console.log(child1) console.log(child2) console.log(person.hoby) // ['唱', '跳', 'rap']let person = { name: 'mjy', age: 19, hoby: ['唱', '跳'], showName() { console.log('my name is: ', this.name) } } let child1 = Object.create(person) child1.name = 'xxt' child1.hoby.push('rap') let child2 = Object.create(person) console.log(child1) console.log(child2) console.log(person.hoby) // ['唱', '跳', 'rap']
優(yōu)點: 不需要單獨創(chuàng)建構(gòu)造函數(shù)。
缺點: 屬性中包含的引用值始終會在相關(guān)對象間共享,子類實例不能向父類傳參
6. 寄生式繼承
寄生式繼承的思路與(寄生) 原型式繼承 和 工廠模式 似, 即創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象,最后再像真的是它做了所有工作一樣返回對象。
function objectCopy(obj) { function Fun() { }; Fun.prototype = obj; return new Fun(); } function createAnother(obj) { let clone = objectCopy(obj); clone.showName = function () { console.log('my name is:', this.name); }; return clone; } let person = { name: "mjy", age: 18, hoby: ['唱', '跳'] } let child1 = createAnother(person); child1.hoby.push("rap"); console.log(child1.hoby); // ['唱', '跳', 'rap'] child1.showName(); // my name is: mjy let child2 = createAnother(person); console.log(child2.hoby); // ['唱', '跳', 'rap']function objectCopy(obj) { function Fun() { }; Fun.prototype = obj; return new Fun(); } function createAnother(obj) { let clone = objectCopy(obj); clone.showName = function () { console.log('my name is:', this.name); }; return clone; } let person = { name: "mjy", age: 18, hoby: ['唱', '跳'] } let child1 = createAnother(person); child1.hoby.push("rap"); console.log(child1.hoby); // ['唱', '跳', 'rap'] child1.showName(); // my name is: mjy let child2 = createAnother(person); console.log(child2.hoby); // ['唱', '跳', 'rap']
優(yōu)點: 寫法簡單,不需要單獨創(chuàng)建構(gòu)造函數(shù)。
缺點: 通過寄生式繼承給對象添加函數(shù)會導(dǎo)致函數(shù)難以重用。使用寄生式繼承來為對象添加函數(shù), 會由于不能做到函數(shù)復(fù)用而降低效率;這一點與構(gòu)造函數(shù)模式類似.
7. 寄生組合式繼承
前面講過,組合繼承是常用的經(jīng)典繼承模式,不過,組合繼承最大的問題就是無論什么情況下,都會調(diào)用兩次父類構(gòu)造函數(shù);一次是在創(chuàng)建子類型的時候,一次是在子類型的構(gòu)造函數(shù)內(nèi)部。寄生組合繼承就是為了降低父類構(gòu)造函數(shù)的開銷而實現(xiàn)的。
通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型,然后再將結(jié)果指定給子類型的原型。
function objectCopy(obj) { function Fun() { }; Fun.prototype = obj; return new Fun(); } function inheritPrototype(child, parent) { let prototype = objectCopy(parent.prototype); prototype.constructor = child; Child.prototype = prototype; } function Parent(name) { this.name = name; this.hoby = ['唱', '跳'] } Parent.prototype.showName = function () { console.log('my name is:', this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } inheritPrototype(Child, Parent); Child.prototype.showAge = function () { console.log('my age is:', this.age); } let child1 = new Child("mjy", 18); child1.showAge(); // 18 child1.showName(); // mjy child1.hoby.push("rap"); console.log(child1.hoby); // ['唱', '跳', 'rap'] let child2 = new Child("yl", 18); child2.showAge(); // 18 child2.showName(); // yl console.log(child2.hoby); // ['唱', '跳']function objectCopy(obj) { function Fun() { }; Fun.prototype = obj; return new Fun(); } function inheritPrototype(child, parent) { let prototype = objectCopy(parent.prototype); prototype.constructor = child; Child.prototype = prototype; } function Parent(name) { this.name = name; this.hoby = ['唱', '跳'] } Parent.prototype.showName = function () { console.log('my name is:', this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } inheritPrototype(Child, Parent); Child.prototype.showAge = function () { console.log('my age is:', this.age); } let child1 = new Child("mjy", 18); child1.showAge(); // 18 child1.showName(); // mjy child1.hoby.push("rap"); console.log(child1.hoby); // ['唱', '跳', 'rap'] let child2 = new Child("yl", 18); child2.showAge(); // 18 child2.showName(); // yl console.log(child2.hoby); // ['唱', '跳']
優(yōu)點: 高效率只調(diào)用一次父構(gòu)造函數(shù),并且因此避免了在子原型上面創(chuàng)建不必要,多余的屬性。與此同時,原型鏈還能保持不變;
缺點: 代碼復(fù)雜
8. ES6類繼承:
在ES6中引入了類的概念,通過class關(guān)鍵字和extends關(guān)鍵字可以實現(xiàn)類的繼承。
class Parent { constructor(name) { this.name = name; } sayHello() { console.log('Hello'); } } class Child extends Parent { constructor(name) { super(name); } } const child = new Child('Child'); console.log(child.name); // Child child.sayHello(); // Hello class Parent { constructor(name) { this.name = name; } sayHello() { console.log('Hello'); } } class Child extends Parent { constructor(name) { super(name); } } const child = new Child('Child'); console.log(child.name); // Child child.sayHello(); // Hello
在這個例子中,我們定義了一個Parent類,通過extends關(guān)鍵字實現(xiàn)子類Child對父類Parent的繼承。子類使用super關(guān)鍵字調(diào)用父類的構(gòu)造函數(shù),并可以訪問父類的屬性和方法。ES6類繼承提供了更加語法簡潔和面向?qū)ο蟮睦^承方式。
以上是JavaScript中常見的八種繼承方式,每種方式都有其特點和適用場景。根據(jù)具體需求,你可以選擇適合的繼承方式來構(gòu)建對象之間的關(guān)系。
總結(jié)
到此這篇關(guān)于JS中常見的8種繼承方法的文章就介紹到這了,更多相關(guān)JS繼承方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
layer實現(xiàn)登錄彈框,登錄成功后關(guān)閉彈框并調(diào)用父窗口的例子
今天小編就為大家分享一篇layer實現(xiàn)登錄彈框,登錄成功后關(guān)閉彈框并調(diào)用父窗口的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09web前端開發(fā)中常見的多列布局解決方案整理(一定要看)
多列布局在web前端開發(fā)中也是較為常見的,今天小編給大家介紹這里會提到的多列布局有兩列定寬加一列自適應(yīng)、多列不定寬加一列自適應(yīng)、多列等分三種,感興趣的朋友一起看看吧2017-10-10JS使用iView的Dropdown實現(xiàn)一個右鍵菜單
這篇文章主要介紹了JS使用iView的Dropdown實現(xiàn)一個右鍵菜單功能,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05Javascript中的window.event.keyCode使用介紹
我們之前發(fā)過不少關(guān)于event.keyCode相關(guān)的文章,大家都可以參考下。2011-04-04基于Bootstrap實現(xiàn)城市三級聯(lián)動
這篇文章主要為大家詳細(xì)介紹了基于BootStrap實現(xiàn)城市三級聯(lián)動,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11