欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript六種繼承方式總結(jié)大全

 更新時間:2025年10月22日 10:15:43   作者:懶羊羊小可愛  
JavaScript中最基本的繼承方式,其核心思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法,下面這篇文章主要介紹了JavaScript六種繼承方式的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

1. 原型鏈繼承

是什么?

這是最基礎的繼承方式。其核心是:讓一個構(gòu)造函數(shù)的 prototype 對象指向另一個構(gòu)造函數(shù)的實例。這樣,子類就能通過原型鏈訪問到父類的屬性和方法。

javascript

// 父類
function Parent() {
    this.name = 'Parent';
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

// 子類
function Child() {
    this.type = 'Child';
}

// 關(guān)鍵!將Child的原型指向Parent的實例,建立原型鏈
Child.prototype = new Parent();

var child1 = new Child();
child1.sayName(); // 'Parent' (來自原型鏈)
console.log(child1.colors); // ['red', 'blue'] (來自原型鏈)

var child2 = new Child();
child1.colors.push('green'); // 修改child1的colors
console.log(child2.colors); // ['red', 'blue', 'green'] (問題出現(xiàn)了!)

有什么作用?

實現(xiàn)屬性和方法的繼承。

實際開發(fā)運用場景?

在現(xiàn)代前端開發(fā)中,幾乎不會單獨使用原型鏈繼承,因為它有致命缺陷。但它是一切其他繼承方式的理論基礎。

優(yōu)點:

  • 簡單易懂,是理解JS繼承的基礎。

  • 能夠繼承父類原型上的方法。

缺點:

  1. 引用類型屬性被所有實例共享(如上例中的 colors 數(shù)組)。一個實例修改了引用類型屬性,所有實例都會受到影響。這是最大的問題。

  2. 創(chuàng)建子類實例時,無法向父類構(gòu)造函數(shù)傳參(因為 new Parent() 在初始化原型時就執(zhí)行了)。

2. 借用構(gòu)造函數(shù)繼承(經(jīng)典繼承)

是什么?

為了解決原型鏈繼承的缺點,這種方法的核心是:在子類構(gòu)造函數(shù)的內(nèi)部調(diào)用父類構(gòu)造函數(shù)。這利用了 call() 或 apply() 方法,使父類的 this 指向子類的實例。

javascript

// 父類
function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
// 注意:父類原型上的方法子類訪問不到
Parent.prototype.sayName = function() {
    console.log(this.name);
};

// 子類
function Child(name, type) {
    // 關(guān)鍵!“借用”父類的構(gòu)造函數(shù)來初始化屬性
    Parent.call(this, name); // 相當于執(zhí)行了 this.name = name; this.colors = ['red', 'blue'];
    this.type = type;
}

var child1 = new Child('小明', 'Child');
var child2 = new Child('小紅', 'Child');

child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
console.log(child2.colors); // ['red', 'blue'] (互不影響了!)
console.log(child1.sayName); // undefined (無法繼承父類原型的方法)

有什么作用?

解決原型鏈繼承中“引用屬性共享”和“無法傳參”的問題。

實際開發(fā)運用場景?

通常不會單獨使用,但它是組合繼承的重要組成部分。

優(yōu)點:

  1. 避免了引用屬性共享的問題。

  2. 可以在子類中向父類傳遞參數(shù)。

缺點:

  1. 方法都在構(gòu)造函數(shù)中定義,每次創(chuàng)建實例都會創(chuàng)建一遍方法,無法實現(xiàn)函數(shù)復用,效率低

  2. 無法繼承父類原型(prototype)上的方法(如上例中的 sayName)。

3. 組合繼承

是什么?

組合繼承結(jié)合了原型鏈繼承借用構(gòu)造函數(shù)繼承的優(yōu)點,是 JavaScript 中最常用的繼承模式。其核心是:

  1. 使用借用構(gòu)造函數(shù)來繼承屬性(解決共享和傳參問題)。

  2. 使用原型鏈來繼承方法(實現(xiàn)方法復用)。

javascript

// 父類
function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

// 子類
function Child(name, type) {
    // 1. 繼承屬性
    Parent.call(this, name); // 第二次調(diào)用 Parent()
    this.type = type;
}

// 2. 繼承方法
Child.prototype = new Parent(); // 第一次調(diào)用 Parent()
// 修復構(gòu)造函數(shù)指向,否則Child實例的constructor會指向Parent
Child.prototype.constructor = Child;
// 子類自己的方法
Child.prototype.sayType = function() {
    console.log(this.type);
};

var child1 = new Child('小明', 'Child1');
child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
child1.sayName(); // '小明'
child1.sayType(); // 'Child1'

var child2 = new Child('小紅', 'Child2');
console.log(child2.colors); // ['red', 'blue'] (屬性不共享)
child2.sayName(); // '小紅' (方法可復用)

有什么作用?

綜合了兩種模式的優(yōu)點,成為 JavaScript 中一種非常實用的繼承模式。

實際開發(fā)運用場景?

在 ES6 的 class 出現(xiàn)之前,這是最主流、最可靠的繼承方式。常用于構(gòu)建復雜的對象系統(tǒng),如UI組件庫、游戲引擎中的實體繼承等。

優(yōu)點:

  1. 實例擁有獨立的屬性,不會共享。

  2. 實例可以復用父類原型上的方法。

  3. 可以向父類構(gòu)造函數(shù)傳參。

缺點:

  • 最大的缺點:會兩次調(diào)用父類構(gòu)造函數(shù)。

    • 一次在 Parent.call(this)

    • 一次在 new Parent()。
      這導致子類實例和原型上存在兩份相同的屬性(一份在實例自身,一份在 __proto__ 里),造成了一些不必要的浪費(雖然實例自身的屬性會屏蔽原型上的屬性,沒有功能問題)。

4. 原型式繼承

是什么?

道格拉斯·克羅克福德提出的方法。其核心是:創(chuàng)建一個臨時的構(gòu)造函數(shù),將其原型指向某個對象,然后返回這個臨時構(gòu)造函數(shù)的實例。本質(zhì)上是對傳入的對象進行了一次淺復制。

javascript

// object() 就是 ES5 中 Object.create() 的模擬實現(xiàn)
function object(o) {
    function F() {} // 創(chuàng)建一個臨時構(gòu)造函數(shù)
    F.prototype = o; // 將其原型指向傳入的對象o
    return new F(); // 返回這個臨時構(gòu)造函數(shù)的實例
}

var person = {
    name: 'Nicholas',
    friends: ['Shelby', 'Court']
};

var person1 = object(person);
person1.name = 'Greg';
person1.friends.push('Rob');

var person2 = object(person);
person2.name = 'Linda';
person2.friends.push('Barbie');

console.log(person.friends); // ['Shelby', 'Court', 'Rob', 'Barbie'] (共享問題依然存在)

有什么作用?

在不必興師動眾地創(chuàng)建構(gòu)造函數(shù)的情況下,基于一個已有對象創(chuàng)建新對象。

實際開發(fā)運用場景?

  • 適用于簡單對象的淺拷貝繼承。

  • ES5 的 Object.create() 方法規(guī)范化了原型式繼承?,F(xiàn)在直接使用 Object.create() 即可。

優(yōu)點:

  • 無需創(chuàng)建構(gòu)造函數(shù),代碼簡潔。

缺點:

  • 和原型鏈繼承一樣,存在引用屬性共享的問題。

5. 寄生式繼承

是什么?

寄生式繼承的思路與寄生構(gòu)造函數(shù)工廠模式類似。其核心是:創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式(如原型式繼承)增強對象,最后返回這個對象。

javascript

function createAnother(original) {
    var clone = object(original); // 1. 通過調(diào)用函數(shù)(如object)創(chuàng)建一個新對象(原型式繼承)
    clone.sayHi = function() {    // 2. 以某種方式來增強這個對象
        console.log('hi');
    };
    return clone; // 3. 返回這個對象
}

var person = {
    name: 'Nicholas',
    friends: ['Shelby', 'Court']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // 'hi'

有什么作用?

主要關(guān)注對象而不是自定義類型和構(gòu)造函數(shù),在主要考慮對象而非自定義類型和構(gòu)造函數(shù)的情況下,實現(xiàn)簡單的繼承和擴展。

實際開發(fā)運用場景?

  • 適用于為對象添加一些額外功能的場景,但不廣泛使用。

優(yōu)點:

  • 可以在不創(chuàng)建構(gòu)造函數(shù)的情況下,為對象添加函數(shù)。

缺點:

  • 函數(shù)難以復用,效率低(跟借用構(gòu)造函數(shù)模式一樣)。

  • 存在引用屬性共享的問題(跟原型式繼承一樣)。

6. 寄生組合式繼承

是什么?

這是組合繼承的優(yōu)化版本,也是目前公認的最理想的繼承范式。它解決了組合繼承調(diào)用兩次父類構(gòu)造函數(shù)的問題。

其核心是:

  1. 使用借用構(gòu)造函數(shù)來繼承屬性。

  2. 使用寄生式繼承繼承父類原型,并將其賦值給子類原型。

javascript

function inheritPrototype(child, parent) {
    // 1. 創(chuàng)建父類原型的一個副本(原型式繼承)
    var prototype = Object.create(parent.prototype);
    // 2. 修復副本的constructor指針
    prototype.constructor = child;
    // 3. 將子類的原型指向這個副本
    child.prototype = prototype;
}

// 父類
function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

// 子類
function Child(name, type) {
    // 只調(diào)用一次父類構(gòu)造函數(shù)(繼承屬性)
    Parent.call(this, name);
    this.type = type;
}

// 關(guān)鍵!替換掉組合繼承中的 `Child.prototype = new Parent()`
inheritPrototype(Child, Parent);

// 添加子類自己的方法
Child.prototype.sayType = function() {
    console.log(this.type);
};

var child1 = new Child('小明', 'Child1');
// 實例的 __proto__ 指向 Child.prototype
// Child.prototype 的 __proto__ 指向 Parent.prototype
// 完美!

有什么作用?

只調(diào)用一次父類構(gòu)造函數(shù),并且避免了在子類原型上創(chuàng)建不必要的、多余的屬性。同時,原型鏈還能保持不變。

寄生組合式繼承核心目標:修復“組合繼承”的缺陷

要理解“寄生組合”,我們必須先回顧“組合繼承”的問題。

組合繼承的做法:

  1. Parent.call(this):在子類構(gòu)造函數(shù)里調(diào)用父類構(gòu)造函數(shù)。這會在子類實例自身上創(chuàng)建一份父類的屬性。

  2. Child.prototype = new Parent():將子類的原型指向一個父類的實例。這會在子類的原型對象上創(chuàng)建第二份父類的屬性。

這就導致了:

  • 子類實例上有 name 和 colors 屬性。

  • 子類實例的 __proto__(也就是 Child.prototype)上也有 name 和 colors 屬性。

雖然實例自身的屬性會屏蔽掉原型上的同名屬性,功能上沒問題,但多創(chuàng)建了一份多余的屬性,造成了內(nèi)存浪費和不優(yōu)雅。

寄生組合式繼承的解決方案

它的核心思路非常巧妙:我們真的需要那個 new Parent() 實例來充當原型嗎?不,我們只需要父類原型上的方法。

我們不需要 Parent 實例上的屬性(因為我們已經(jīng)通過 Parent.call(this) 在子類實例上得到了一份),我們只需要能通過原型鏈找到 Parent.prototype 上的方法。

所以,新的方案是:

  1. 繼承屬性:保持不變,依然在子類構(gòu)造函數(shù)里用 Parent.call(this)子類構(gòu)造函數(shù)里調(diào)用父類構(gòu)造函數(shù),這會在子類實例自身上創(chuàng)建一份父類的屬性。這保證了每個實例都有自己獨立的屬性。

  2. 繼承方法不再用 new Parent(),而是直接創(chuàng)建一個純凈的、指向父類原型的對象,用它來作為子類的原型。

這個“純凈的、指向父類原型的對象”就是 Object.create(Parent.prototype) 所做的事情。

一步步拆解(超級詳細版)

讓我們把 inheritPrototype(Child, Parent) 這個函數(shù)里的三步拆開來看:

javascript

function inheritPrototype(child, parent) {
    // 第一步:創(chuàng)建原型副本(核心)
    var prototype = Object.create(parent.prototype);
    // 第二步:修復constructor指向
    prototype.constructor = child;
    // 第三步:將子類的原型指向這個新創(chuàng)建的對象
    child.prototype = prototype;
}

第一步:var prototype = Object.create(parent.prototype);

  • Object.create() 方法會創(chuàng)建一個新對象。

  • 這個新對象的 __proto__ 會指向你傳入的參數(shù),也就是 parent.prototype。

  • 想象一下:這就好比我們憑空造了一個空對象 {},并且讓這個空對象“認” Parent.prototype 為它的爸爸(原型)。這個空對象自己沒有任何屬性(解決了屬性重復的問題),但它可以順著原型鏈找到 Parent.prototype 上的所有方法(sayName)。

第二步:prototype.constructor = child;

  • 任何一個 prototype 對象都有一個 constructor 屬性,默認指向它關(guān)聯(lián)的構(gòu)造函數(shù)。

  • 因為我們用 Object.create() 創(chuàng)建的新對象,它的 constructor 指向的是 parent(因為它繼承自 parent.prototype,而 parent.prototype.constructor 指向 parent)。

  • 這顯然不對,我們希望子類原型的 constructor 指向子類自己 child

  • 所以我們需要手動糾正一下,讓 prototype.constructor = child;。

第三步:child.prototype = prototype;

  • 最后,我們把這個我們精心制作好的、純凈的、鏈接到了父類原型的、constructor指正確的 prototype 對象,賦值給子類的 prototype。

  • 從此,所有 new Child() 出來的實例,它們的 __proto__ 都指向我們這個 prototype 對象,從而可以順利地通過原型鏈調(diào)用父類的方法。

終極比喻:“繼承家產(chǎn)”的故事

  • 父類 (Parent):一個富豪老爹,他有金庫(實例屬性 namecolors)和一本生意經(jīng)(原型方法 sayName)。

  • 組合繼承:老爹先給你復制了一本完整的生意經(jīng)(包括金庫的地圖),然后你又自己去金庫里拿了一次金子。你手里有金子,書上也有金子的地圖,地圖是多余的。

  • 寄生組合繼承:一個聰明的律師(Object.create)出現(xiàn)了。他沒有復制整本生意經(jīng),而是只做了一張神奇的索引卡。這張索引卡本身是空白的(沒有多余的屬性),但它直接指向了老爹那本生意經(jīng)的原始內(nèi)容(Parent.prototype)。然后你又自己去金庫里拿了一次金子。你手里有金子,也通過索引卡學會了老爹的生意經(jīng),完美!

總結(jié):為什么它是最佳的?

特性組合繼承寄生組合繼承優(yōu)勢
父類屬性副本2份 (實例上1份,原型上1份)1份 (僅在實例上)更高效,無浪費
父類方法繼承通過原型鏈繼承通過原型鏈繼承同樣有效
父類構(gòu)造函數(shù)調(diào)用2次1次性能更優(yōu)
原型鏈保持正確保持正確同樣正確

所以,寄生組合式繼承的核心貢獻就是:
它用一種極其巧妙的方式(Object.create)建立了子類和父類原型的直接聯(lián)系,完全跳過了創(chuàng)建父類實例這個不必要的步驟,從而避免了創(chuàng)建多余的屬性,完美地實現(xiàn)了繼承。這也是為什么ES6的 class 和 extends 語法糖其底層實現(xiàn)原理是寄生組合繼承的原因,因為它確實是理論上最完美的方案

實際開發(fā)運用場景?

這是實現(xiàn)基于構(gòu)造函數(shù)的繼承的最佳模式。在需要高度優(yōu)化和避免不必要的內(nèi)存開銷的庫或框架中可能會看到。但在日常開發(fā)中,我們更傾向于使用 ES6 的 class 和 extends,其底層原理就是寄生組合式繼承。

優(yōu)點:

  • 只調(diào)用一次父類構(gòu)造函數(shù),效率高。

  • 避免了在子類原型上創(chuàng)建不必要的屬性。

  • 原型鏈保持不變,能正常使用 instanceof 和 isPrototypeOf。

缺點:

  • 實現(xiàn)起來相對復雜。但通??梢苑庋b成一個函數(shù)(如上面的 inheritPrototype)來復用。

總結(jié)與建議

繼承方式核心思想優(yōu)點缺點適用場景
原型鏈繼承子類原型 = 父類實例簡單,方法可復用引用共享,無法傳參基礎學習,幾乎不用
借用構(gòu)造函數(shù)在子類中 Parent.call(this)屬性獨立,可傳參方法不能復用組合繼承的一部分
組合繼承借用構(gòu)造 + 原型鏈屬性獨立,方法可復用,可傳參調(diào)用兩次父類構(gòu)造函數(shù)ES6前的主流方式
原型式繼承Object.create()無需構(gòu)造函數(shù),簡單引用共享對象淺拷貝
寄生式繼承工廠模式+增強對象無需構(gòu)造函數(shù),可增強對象方法不能復用,引用共享為對象添加功能
寄生組合繼承借用構(gòu)造 + 寄生式繼承父類原型近乎完美,只調(diào)用一次父類構(gòu)造函數(shù)實現(xiàn)稍復雜理想的繼承范式

現(xiàn)代開發(fā)建議:

直接使用 ES6 的 class 和 extends 關(guān)鍵字。它們的語法更清晰、更易于理解,并且其底層實現(xiàn)的就是寄生組合式繼承這種最理想的方式。你不再需要手動處理原型鏈,避免了出錯的可能。

javascript

// ES6 的寫法,底層是寄生組合式繼承
class Parent {
    constructor(name) {
        this.name = name;
        this.colors = ['red', 'blue'];
    }
    sayName() {
        console.log(this.name);
    }
}

class Child extends Parent {
    constructor(name, type) {
        super(name); // 相當于 Parent.call(this, name)
        this.type = type;
    }
    sayType() {
        console.log(this.type);
    }
}

到此這篇關(guān)于JavaScript六種繼承方式的文章就介紹到這了,更多相關(guān)JS六種繼承方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js parseInt("08")未指定進位制問題

    js parseInt("08")未指定進位制問題

    今天在做JS關(guān)于月份的判斷,對于parseInt("01")到parseInt("07");都能得到正確的結(jié)果,但如果是parseInt("08")或parseInt("09")則返回0,首先看parseInt語法:parseInt(string, radix);
    2010-06-06
  • Bootstrap 響應式實用工具實例詳解

    Bootstrap 響應式實用工具實例詳解

    Bootstrap 提供了一些輔助類,以便更快地實現(xiàn)對移動設備友好的開發(fā)。這些可以通過媒體查詢結(jié)合大型、小型和中型設備,實現(xiàn)內(nèi)容對設備的顯示和隱藏。下面通過本文給大家分享Bootstrap 響應式實用工具,一起看看吧
    2017-03-03
  • Javascript之面向?qū)ο?-接口

    Javascript之面向?qū)ο?-接口

    本篇文章實例演示了Javascript的面向?qū)ο?-接口的用法。希望對大家有所幫助,下面就隨小編一起來看看吧
    2016-12-12
  • 如何寫出一個驚艷面試官的JavaScript深拷貝

    如何寫出一個驚艷面試官的JavaScript深拷貝

    淺拷貝是面試中經(jīng)常會被問到的問題,這篇文章主要給大家介紹了關(guān)于如何寫出一個驚艷面試官的JavaScript深拷貝的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-05-05
  • JS?連接MQTT的使用方法

    JS?連接MQTT的使用方法

    這篇文章主要介紹了JS?連接MQTT的使用方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01
  • javascript天然的迭代器

    javascript天然的迭代器

    有一個數(shù)n=5,不用for循環(huán),怎么返回[1,2,3,4,5]這樣一個數(shù)組
    2010-10-10
  • javascript特殊日歷控件分享

    javascript特殊日歷控件分享

    這篇文章主要為大家詳細介紹了javascript特殊日歷控件的使用方法,展示了javascript日歷控件實現(xiàn)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • footer定位頁面底部(代碼分享)

    footer定位頁面底部(代碼分享)

    本文主要分享了footer定位頁面底部的實例代碼,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03
  • 將input框中輸入內(nèi)容顯示在相應的div中【三種方法可選】

    將input框中輸入內(nèi)容顯示在相應的div中【三種方法可選】

    本篇文章主要介紹了在input框中輸入內(nèi)容,會相應的顯示在下面的div中的不同做法:js方法;jQuery方法;AngularJs方法,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-05-05
  • Js數(shù)組排序函數(shù)sort()介紹

    Js數(shù)組排序函數(shù)sort()介紹

    本文給大家簡單探討下Js數(shù)組排序函數(shù)sort()的用法和示例,有需要的小伙伴可以參考下。
    2015-06-06

最新評論