深入理解JS面向?qū)ο?- JavaScript實(shí)現(xiàn)繼承的五種方式與優(yōu)缺點(diǎn)

一、類(lèi)與實(shí)例

1. 類(lèi)的聲明

  1. 傳統(tǒng)function類(lèi)的聲明
function Animal() {
  this.name = 'name';
}
  1. ES6中的class聲明
class Animal2 {
  //構(gòu)造函數(shù)
  constructor (){
    this.name = name;
  }
}
1.1 ES6 class與ES5 function的區(qū)別:

Class的特點(diǎn):
Class在語(yǔ)法上更加貼合面向?qū)ο蟮膶?xiě)法
Class實(shí)現(xiàn)繼承更加易讀,更加容易理解
更易于寫(xiě)Java等后端語(yǔ)言的使用
本質(zhì)還是是語(yǔ)法糖,使用prototype

  1. 重復(fù)定義
  • function會(huì)覆蓋之前定義的方法
  • class會(huì)報(bào)錯(cuò)
  1. 構(gòu)造器 constructor
  • 在function定義的構(gòu)造函數(shù)中,其prototype.constructor屬性指向構(gòu)造器自身
  • 在class定義的類(lèi)中,constructor也相當(dāng)于定義在prototype屬性上
  1. 原型或者類(lèi)中方法的枚舉
  • class中定義的方法不可用Object.keys(Point.prototype)枚舉到
  • function構(gòu)造器原型方法可被Object.keys(Point.prototype)枚舉到,除了constructor
  • 所有原型方法屬性都可用Object.getOwnPropertyNames(Point.prototype)訪(fǎng)問(wèn)到
  • 不管是class還是function,constructor屬性默認(rèn)不可枚舉

2. 生成實(shí)例

如何通過(guò)類(lèi)實(shí)例類(lèi)的對(duì)象

  console.log(new Animal(), new Animal2());

注:如果構(gòu)造函數(shù)沒(méi)有聲明的話(huà),new函數(shù)名后面的括號(hào)可以省略

二、類(lèi)與繼承

2.1 如何實(shí)現(xiàn)繼承

2.2 繼承的幾種方式

  1. 借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承
    原理:在子類(lèi)的函數(shù)體內(nèi)執(zhí)行父類(lèi),用call和apply都可以改變函數(shù)運(yùn)行的上下文,導(dǎo)致父類(lèi)執(zhí)行的實(shí)例都會(huì)掛載到子類(lèi)上面。
function Parent1 () {
  this.name = 'parent1';
}
Parent1.prototype.say = function () {};/*new Child1().say() 報(bào)錯(cuò)*/
function Child1 () {
  Parent1.call(this);/*apply也可以改變函數(shù)運(yùn)行的上下文*/
  this.type = 'child1';
}

控制臺(tái)輸出:

console.log(new Child1);

缺點(diǎn):構(gòu)造函數(shù)除了函數(shù)體里面的內(nèi)容,還可能有原型鏈上的,但是這種方式中構(gòu)造函數(shù)原型鏈上的東西并沒(méi)有被繼承。
擴(kuò)展學(xué)習(xí):javascript中apply、call和bind方法的區(qū)別

  1. 借助原型鏈實(shí)現(xiàn)繼承(彌補(bǔ)構(gòu)造函數(shù)繼承不足)
function Parent2() {
    this.name = 'parent2';
    this.play = [1, 2, 3];
}

function Child2() {
    this.type = 'child2';
}
Child2.prototype = new Parent2();
//缺點(diǎn)
console.log(new Child2());
var o1 = new Child2();
var o2 = new Child2();
console.log(o1.play, o2.play);//都為1,2,3

o1.play.push(4);//將o1重新復(fù)制

console.log(o1.play, o2.play);//都為1,2,3,4

實(shí)現(xiàn)原理:低級(jí)構(gòu)造函數(shù)的原型是高級(jí)構(gòu)造函數(shù)的實(shí)例,new Child2().__proto__就是Parent2父類(lèi)的一個(gè)實(shí)例對(duì)象。即new Child2().__proto__ === Parent2.prototypetrue。new Child2().__proto__.name的值為parent2。
缺點(diǎn):因?yàn)樵玩溨械脑褪?strong>共用的,所以?xún)蓚€(gè)對(duì)象不隔離。改變一個(gè)影響另一個(gè)。o1.__proto__ = o2.__proto__。

  1. 組合繼承方式(構(gòu)造函數(shù)+原型鏈)
function Parent3() {
    this.name = 'Parent3';
    this.play = [1, 2, 3];
}

function Child3() {
    Parent3.call(this);
    this.type = 'Child3';
}
Child3.prototype = new Parent3();
//檢驗(yàn)
console.log(new Child3());
var o1 = new Child3();
var o2 = new Child3();

o1.play.push(4);
console.log(o1.play, o2.play);//這時(shí)候結(jié)果就不一樣了 分別是 [1,2,3,4] [1,2,3]

缺點(diǎn):實(shí)例化子類(lèi)的時(shí)候父類(lèi)構(gòu)造函數(shù)執(zhí)行兩次。是沒(méi)有必要的。

  1. 組合繼承優(yōu)化一
function Parent4() {
    this.name = 'Parent4';
    this.play = [1, 2, 3];
}

function Child4() {
    Parent4.call(this);
    this.type = 'Child4';
}
Child4.prototype = Parent4.prototype;
console.log(new Child4());
var o1 = new Child4();
var o2 = new Child4();

o1.play.push(4);
console.log(o1.play, o2.play);
console.log(o1 instanceof Child4, s5 instanceof Parent4);//true, true
console.log(o1.constructor);

怎么區(qū)分,是否是直接實(shí)例化的?
直接拿的父類(lèi)實(shí)例

  1. 組合繼承優(yōu)化二
function Parent5() {
    this.name = 'Parent5';
    this.play = [1, 2, 3];
}

function Child5() {
    Parent5.call(this);
    this.type = 'Child5';
}
Child5.prototype = Object.create(Parent5.prototype);

創(chuàng)建中間對(duì)象,完美~

2.3 Class和普通構(gòu)造函數(shù)實(shí)現(xiàn)繼承對(duì)比

Class實(shí)現(xiàn)(ES6)
class Animal {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat`)
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name)
        this.name = name
    }
    say() {
        console.log(`${this.name} say`)
    }
}
const dog = new Dog('husky')
dog.say()
dog.eat()
普通構(gòu)造函數(shù)實(shí)現(xiàn)
function Animal() {
    this.eat = function () {
        alert('Animal eat')
    }

}

function Dog() {
    this.bark = function () {
        alert('Dog bark')
    }
}

Dog.prototype = new Animal()
var husky = new Dog()

husky.bark()
husky.eat()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容