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

JS中的六種繼承方式以及優(yōu)缺點(diǎn)總結(jié)

 更新時(shí)間:2021年10月11日 12:49:33   作者:Nordon  
JS作為面向?qū)ο蟮娜躅愋驼Z言,繼承也是其非常強(qiáng)大的特性之一,那么如何在JS中實(shí)現(xiàn)繼承呢?下面這篇文章主要給大家介紹了關(guān)于JS中六種繼承方式以及優(yōu)缺點(diǎn)的相關(guān)資料,需要的朋友可以參考下

前言

繼承是JS世界中必不可少的一個(gè)環(huán)節(jié),號(hào)稱JS的三座大山之一,使用這種方式我們可以更好地復(fù)用以前的開發(fā)代碼,縮短開發(fā)的周期、提升開發(fā)效率

在ES6之前,JS中的類都是通過構(gòu)造函數(shù)模擬的,并不存在真正意義上的類,雖然ES6的類知識(shí)一個(gè)語法糖😂,這個(gè)時(shí)期的類是可以當(dāng)作函數(shù)直接使用的,到了ES6之后,類是不可以再當(dāng)作函數(shù)使用了

在開始聊繼承之前,首先需要明確的是類中存在兩種屬性:實(shí)例上的屬性和公共屬性,接下來談到的所有繼承方式都是圍繞這兩點(diǎn)來展開

function Animal(name) {
  // 實(shí)例上的屬性
  this.name = name;
}

// 公共屬性
Animal.prototype.eat = function() {
  // todo ...
}

如何避免將ES6之前的構(gòu)造函數(shù)直接當(dāng)作函數(shù)調(diào)用?

ES5時(shí)期解決方案:

function Animal() {
    // 若是直接調(diào)用, 不是使用 new調(diào)用, 拋出異常
    if (!(this instanceof Animal)) {
        // new的原理, 使用new的時(shí)候, this是Animal的實(shí)例, 則 this instanceof Animal 為true
        throw new Error("請(qǐng)勿直接調(diào)用構(gòu)造函數(shù)");
    }
}

ES6之后解決方案:

function Animal() {
    // 若是使用new, 則new.target指向自身, 否則為undefined, 但是在繼承的時(shí)候不能使用,因?yàn)槔^承實(shí)例上屬性的時(shí)候, 原來的es5是使用 Animal.call(this)的方式
    if (!new.target) {
        throw new Error("請(qǐng)勿直接調(diào)用構(gòu)造函數(shù)");
    }
}

上述兩種方案都可以解決直接當(dāng)作函數(shù)調(diào)用,若是直接調(diào)用控制臺(tái)會(huì)報(bào)錯(cuò):Uncaught Error: 請(qǐng)勿直接調(diào)用構(gòu)造函數(shù)

接下來便一起看看JS中有哪些繼承方式

原型鏈繼承

原型鏈繼承是比較常見的繼承方式之一,其中涉及的構(gòu)造函數(shù)、原型和實(shí)例,三者之間存在著一定的關(guān)系,即每一個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象又包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例則包含一個(gè)原型對(duì)象的指針。

function Person(name) {
    this.name = name;
    this.permission = ["user", "salary", "vacation"];
}

Person.prototype.say = function () {
    console.log(`${this.name} 說話了`);
};

function Staff(age) {
    this.age = age;
}

Staff.prototype = new Person("張三");

const zs = new Staff(12);
console.log(zs.name); // 張三
zs.say(); // 張三 說話了

此時(shí)代碼是符合期望,接下來再創(chuàng)建一個(gè)實(shí)例并修改name和permission

const zs = new Staff(12);
const zs2 = new Staff(18);
zs.permission.pop()
zs.name = '李四';

console.log(zs.name);
console.log(zs2.name);
console.log(zs.permission);
console.log(zs2.permission);

前兩個(gè)分別輸出是:李四、張三,后面兩個(gè)輸出結(jié)果一致,都為["user", "salary"],為什么會(huì)出現(xiàn)這種情況呢?
當(dāng)執(zhí)行zs.name = '李四';時(shí),其實(shí)這個(gè)時(shí)候是賦值操作,賦值之后zs變?yōu)?/p>

而zs2.name是通過原型鏈繼續(xù)查找,因此前面的兩個(gè)輸出是李四、張三

通過console.log(zs.__proto__ === zs2.__proto__);輸出為true,可以得知兩個(gè)實(shí)例使用的是同一個(gè)原型對(duì)象Person,他們的內(nèi)存空間是共享的,當(dāng)一個(gè)發(fā)生變化時(shí),另外一個(gè)也隨之進(jìn)行了變化

通過上述發(fā)現(xiàn)原型鏈繼承存在一些缺點(diǎn)

構(gòu)造函數(shù)繼承

構(gòu)造函數(shù)通常時(shí)借助call、apply來完成繼承

function Person(name) {
    this.name = name;
    this.permission = ["user", "salary", "vacation"];
}

Person.prototype.say = function () {
    console.log(`${this.name} 說話了`);
};

function Staff(name, age) {
    Person.call(this, name);
    this.age = age;
}

Staff.prototype.eat = function () {
    console.log('吃東西啦~~~');
}

const zs = new Staff("張三", 12);
console.log(zs);

上述代碼控制臺(tái)輸出:

可以看到不僅擁有Staff的屬性和方法,同時(shí)也繼承了Person的屬性,因?yàn)槊看螌?shí)例化的時(shí)候都會(huì)調(diào)用Person.call(this, name);,可以解決原型鏈繼承的問題

此時(shí)調(diào)用Person原型上的方法

zs.say()

這個(gè)時(shí)候控制臺(tái)會(huì)報(bào)錯(cuò):Uncaught TypeError: zs.say is not a function

組合繼承(原型鏈繼承和構(gòu)造函數(shù)繼承組合)

原型鏈繼承和構(gòu)造函數(shù)繼承都存在各自的問題和優(yōu)勢(shì),結(jié)合兩種繼承方式便生成了組合繼承

function Person(name) {
    this.name = name;
    this.permission = ["user", "salary", "vacation"];
}

Person.prototype.say = function () {
	console.log(`${this.name} 說話了`);
};

function Staff(name, age) {
    // 第二次執(zhí)行 Person
    Person.call(this, name);
    this.age = age;
}

Staff.prototype.eat = function () {
    console.log("吃東西啦~~~");
};

// 第一次執(zhí)行 Person
Staff.prototype = new Person();
// 若是不將Staff constructor指回到Staff, 此時(shí)的Staff實(shí)例zs.constructor則指向Person
Staff.prototype.constructor = Staff;

const zs = new Staff("張三", 12);
const ls = new Staff("李四", 12);
zs.permission.pop();
console.log(zs.permission);
console.log(ls.permission);
zs.say();
ls.say();

暫時(shí)控制臺(tái)的輸出都是正常的,也將上述兩種繼承的缺點(diǎn)解決了,但是此時(shí)又新增了兩個(gè)個(gè)問題:

  1. Person被執(zhí)行了兩次,分別為:Person.call(this, name)和new Person(),期望執(zhí)行一次,多執(zhí)行的一次便會(huì)造成一次性能開銷
  2. 在之前Staff.prototype = new Person()定義一些公共屬性和方法時(shí)會(huì)被覆蓋掉,例如不能實(shí)例調(diào)用zs.eat(),控制臺(tái)會(huì)報(bào)錯(cuò)Uncaught TypeError: zs.eat is not a function,若是在其之后定義則會(huì)污染Person

寄生式繼承

通過利用Object.create獲得一份目標(biāo)對(duì)象的淺拷貝,然后添加一些方法避免污染基類,主要是解決組合繼承的第二個(gè)問題

主要將如下兩行代碼進(jìn)行替換

Staff.prototype = new Person();
Staff.prototype.constructor = Staff;

替換為:

Staff.prototype = Object.create(Person.prototype, {
    constructor: {
        // 若是不將Staff constructor指回到Staff, 此時(shí)的Staff實(shí)例zs.constructor則指向Person
        value: Staff,
    },
});

組合寄生式繼承

到目前為止,還有一個(gè)兩次實(shí)例化Person的問題沒有解決,接下來的組合寄生式繼承可完美解決上述問題,這也是ES6之前所有繼承方式中最優(yōu)的繼承方式

完整代碼如下:

function Person(name) {
    this.name = name;
    this.permission = ["user", "salary", "vacation"];
}

Person.prototype.say = function () {
    console.log(`${this.name} 說話了`);
};

function Staff(name, age) {
    Person.call(this, name);
    this.age = age;
}

Staff.prototype = Object.create(Person.prototype, {
    constructor: {
        // 若是不將Staff constructor指回到Staff, 此時(shí)的Staff實(shí)例zs.constructor則指向Person
        value: Staff,
    },
});

Staff.prototype.eat = function () {
    console.log("吃東西啦~~~");
};

其實(shí)繼承時(shí),改變Staff.prototype指向并不止上述這些方式,還有一些其他方法

  • prototype.__proto__方式
Staff.prototype.__proto__ = Person.prototype

prototype.__proto__存在兼容性問題,自己找不到,通過原型鏈繼續(xù)向上查找,此時(shí)Animal和Tiger不會(huì)再共享同一地址,不會(huì)相互影響

  • Object.setPrototypeOf方式
Object.setPrototypeOf(Staff.prototype, Person.prototype)

es6語法, 存在兼容性,其原理就是原理就是 prototype.__proto__方式

extends繼承

在ES6之后,可以使用extends進(jìn)行繼承,這也是目前開發(fā)中最常使用的方式,雖然目前瀏覽器支持度并不理想,但是在工程化如此完善的今天,這些都已經(jīng)不是制約使用其的理由

class Person {
    constructor(name) {
        this.name = name;
        this.permission = ["user", "salary", "vacation"];
    }

    say() {
        console.log(`${this.name} 說話了`);
    }
}

class Staff extends Person {
    constructor(name, age) {
        super(name);
        this.age = age;
    }

    eat() {
        console.log("吃東西啦~~~");
    }
}

其實(shí)ES6的繼承通過babel編譯之后,采用也是組合寄生式繼承,因此我們需要重點(diǎn)掌握其繼承原理。

總結(jié)

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

相關(guān)文章

  • javascript實(shí)現(xiàn)焦點(diǎn)滾動(dòng)圖效果 具體方法

    javascript實(shí)現(xiàn)焦點(diǎn)滾動(dòng)圖效果 具體方法

    以下JS代碼實(shí)現(xiàn)了焦點(diǎn)滾動(dòng)圖的效果方法,有需要的朋友可以參考一下
    2013-06-06
  • JavaScript返回0-1之間隨機(jī)數(shù)的方法

    JavaScript返回0-1之間隨機(jī)數(shù)的方法

    這篇文章主要介紹了JavaScript返回0-1之間隨機(jī)數(shù)的方法,涉及javascript中Math對(duì)象random方法的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • 利用JavaScript編寫一個(gè)簡(jiǎn)單的1024小游戲

    利用JavaScript編寫一個(gè)簡(jiǎn)單的1024小游戲

    在每年的10月24日,我們都會(huì)慶祝程序員節(jié),這是一個(gè)向所有辛勤工作、創(chuàng)造出無數(shù)令人驚嘆應(yīng)用和系統(tǒng)的程序員們致敬的日子,為了紀(jì)念這個(gè)特殊的日子,我們將通過編寫一個(gè)簡(jiǎn)單的1024小游戲來向所有程序員們表示敬意,本文將詳細(xì)解釋如何使用JavaScript編寫這個(gè)小游戲
    2023-10-10
  • JS實(shí)用的動(dòng)畫彈出層效果實(shí)例

    JS實(shí)用的動(dòng)畫彈出層效果實(shí)例

    這篇文章主要介紹了JS實(shí)用的動(dòng)畫彈出層效果,實(shí)例分析了javascript實(shí)現(xiàn)動(dòng)畫效果彈出層的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-05-05
  • vue 集成騰訊地圖實(shí)現(xiàn)api(附DEMO)

    vue 集成騰訊地圖實(shí)現(xiàn)api(附DEMO)

    之前項(xiàng)目使用騰訊地圖,不利于開發(fā)者查找,這篇文章主要介紹了vue 集成騰訊地圖實(shí)現(xiàn)api,具有一定的參考價(jià)值,感興趣的可以了解下
    2021-07-07
  • JS Array創(chuàng)建及concat()split()slice()的使用方法

    JS Array創(chuàng)建及concat()split()slice()的使用方法

    下面小編就為大家?guī)硪黄狫S Array創(chuàng)建及concat()split()slice()的使用方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • JS使用onerror捕獲異常示例

    JS使用onerror捕獲異常示例

    這篇文章主要介紹了JS使用onerror捕獲異常的方法,結(jié)合實(shí)例形式分析了javascript基于onerror事件處理器捕獲異常的具體流程與相關(guān)操作技巧,需要的朋友可以參考下
    2016-08-08
  • JS使用單鏈表統(tǒng)計(jì)英語單詞出現(xiàn)次數(shù)

    JS使用單鏈表統(tǒng)計(jì)英語單詞出現(xiàn)次數(shù)

    這篇文章主要為大家詳細(xì)介紹了JS使用單鏈表統(tǒng)計(jì)英語單詞出現(xiàn)次數(shù)的相關(guān)資料,列出所有單詞及其出現(xiàn)次數(shù),感興趣的小伙伴們可以參考一下
    2016-06-06
  • Js中parentNode,parentElement,childNodes,children之間的區(qū)別

    Js中parentNode,parentElement,childNodes,children之間的區(qū)別

    這篇文章主要是對(duì)Js中parentNode,parentElement,childNodes,children的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助
    2013-11-11
  • 用原生JS對(duì)AJAX做簡(jiǎn)單封裝的實(shí)例代碼

    用原生JS對(duì)AJAX做簡(jiǎn)單封裝的實(shí)例代碼

    下面小編就為大家?guī)硪黄迷鶭S對(duì)AJAX做簡(jiǎn)單封裝的實(shí)例代碼。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-07-07

最新評(píng)論