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

JavaScript中常見(jiàn)的七種繼承及實(shí)現(xiàn)

 更新時(shí)間:2023年03月08日 08:56:51   作者:前端技術(shù)棧  
JS的繼承方式在我們面試的時(shí)候經(jīng)常會(huì)被問(wèn)到,所以深入理解js繼承方式以及它們的優(yōu)缺點(diǎn)是非常有必要的。本文為大家整理了JavaScript中常見(jiàn)的七種繼承及實(shí)現(xiàn),需要的可以參考一下

1. 原型鏈繼承

原型鏈繼承是 JavaScript 中一種基于原型的繼承方式,它通過(guò)將一個(gè)構(gòu)造函數(shù)的實(shí)例作為另一個(gè)構(gòu)造函數(shù)的原型,從而實(shí)現(xiàn)繼承。具體來(lái)說(shuō),就是在子類(lèi)的構(gòu)造函數(shù)中通過(guò) Child.prototype = new Parent() 的方式來(lái)繼承父類(lèi)的屬性和方法。

以下是一個(gè)實(shí)現(xiàn)原型鏈繼承的示例代碼:

// 定義父類(lèi)構(gòu)造函數(shù)functionParent(name) {
  this.name = name;
}
 
// 父類(lèi)原型上的方法Parent.prototype.sayName = function() {
  console.log('My name is ' + this.name);
}
 
// 定義子類(lèi)構(gòu)造函數(shù)functionChild(name, age) {
  this.age = age;
}
 
// 將子類(lèi)的原型設(shè)置為父類(lèi)的實(shí)例Child.prototype = newParent();
 
// 子類(lèi)原型上的方法Child.prototype.sayAge = function() {
  console.log('I am ' + this.age + ' years old');
}
 
// 創(chuàng)建子類(lèi)實(shí)例var child = newChild('Tom', 10);
 
// 調(diào)用子類(lèi)實(shí)例的方法
child.sayName(); // My name is Tom
child.sayAge(); // I am 10 years old復(fù)制代碼

在上面的示例代碼中,我們首先定義了一個(gè)父類(lèi)構(gòu)造函數(shù) Parent,并在其原型上定義了一個(gè)方法 sayName。然后我們定義了一個(gè)子類(lèi)構(gòu)造函數(shù) Child,并通過(guò)將子類(lèi)的原型設(shè)置為父類(lèi)的實(shí)例來(lái)實(shí)現(xiàn)繼承。最后我們創(chuàng)建了一個(gè)子類(lèi)實(shí)例 child,并調(diào)用其方法來(lái)驗(yàn)證繼承是否成功。

優(yōu)點(diǎn):

  • 簡(jiǎn)單易懂:原型鏈繼承是一種簡(jiǎn)單的繼承方式,易于理解和實(shí)現(xiàn)。
  • 父類(lèi)方法更新會(huì)自動(dòng)同步到子類(lèi)實(shí)例:由于子類(lèi)實(shí)例的原型指向父類(lèi)實(shí)例,所以父類(lèi)的方法更新會(huì)自動(dòng)同步到子類(lèi)實(shí)例中。
  • 可以重用父類(lèi)方法:由于子類(lèi)實(shí)例可以訪(fǎng)問(wèn)父類(lèi)的原型,因此可以重用父類(lèi)的方法,從而減少代碼量。

缺點(diǎn):

  • 所有子類(lèi)實(shí)例共享原型對(duì)象:由于所有子類(lèi)實(shí)例的原型都指向同一個(gè)對(duì)象,因此一個(gè)實(shí)例對(duì)原型對(duì)象的修改會(huì)影響到其他實(shí)例。
  • 無(wú)法向父類(lèi)構(gòu)造函數(shù)傳遞參數(shù):原型鏈繼承無(wú)法向父類(lèi)構(gòu)造函數(shù)傳遞參數(shù),因此子類(lèi)實(shí)例無(wú)法向父類(lèi)構(gòu)造函數(shù)傳遞參數(shù),也無(wú)法對(duì)父類(lèi)實(shí)例進(jìn)行初始化。
  • 無(wú)法實(shí)現(xiàn)多繼承:由于JavaScript中一個(gè)對(duì)象只能有一個(gè)原型對(duì)象,因此原型鏈繼承無(wú)法實(shí)現(xiàn)多繼承。

2. 借用構(gòu)造函數(shù)繼承

JavaScript中的借用構(gòu)造函數(shù)繼承是一種通過(guò)調(diào)用父類(lèi)構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)繼承的方式。這種繼承方式有以下特點(diǎn):

子類(lèi)實(shí)例擁有了父類(lèi)構(gòu)造函數(shù)中定義的屬性和方法。

子類(lèi)實(shí)例與父類(lèi)實(shí)例之間不存在原型鏈的關(guān)系,因此可以避免共享原型對(duì)象帶來(lái)的問(wèn)題。

子類(lèi)無(wú)法重用父類(lèi)原型對(duì)象上的方法。

以下是一個(gè)借用構(gòu)造函數(shù)繼承的示例代碼:

functionAnimal(name) {
  this.name = name;
}
 
Animal.prototype.sayName = function() {
  console.log('My name is ' + this.name);
}
 
functionDog(name, age) {
  Animal.call(this, name); // 借用Animal構(gòu)造函數(shù),并將this指向Dog實(shí)例this.age = age;
}
 
let dog1 = newDog('旺財(cái)', 2);
let dog2 = newDog('小白', 1);
 
console.log(dog1.name); // '旺財(cái)'console.log(dog2.age); // 1
dog1.sayName(); // TypeError: dog1.sayName is not a function復(fù)制代碼

在上面的代碼中,Dog類(lèi)通過(guò)調(diào)用Animal構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)繼承,從而擁有了Animal類(lèi)中定義的屬性和方法。由于子類(lèi)實(shí)例與父類(lèi)實(shí)例之間不存在原型鏈的關(guān)系,因此修改一個(gè)實(shí)例的屬性不會(huì)影響到其他實(shí)例。

但是需要注意的是,由于子類(lèi)實(shí)例無(wú)法訪(fǎng)問(wèn)父類(lèi)原型對(duì)象上的方法,因此在上面的代碼中,dog1實(shí)例調(diào)用sayName()方法會(huì)報(bào)錯(cuò)。如果需要在子類(lèi)中重用父類(lèi)原型對(duì)象上的方法,可以考慮使用組合繼承或寄生組合式繼承。

優(yōu)點(diǎn):

  • 可以向父類(lèi)構(gòu)造函數(shù)傳遞參數(shù):借用構(gòu)造函數(shù)繼承可以向父類(lèi)構(gòu)造函數(shù)傳遞參數(shù),從而可以對(duì)父類(lèi)實(shí)例進(jìn)行初始化。
  • 避免了原型對(duì)象共享的問(wèn)題:由于借用構(gòu)造函數(shù)繼承創(chuàng)建了一個(gè)新的對(duì)象,因此避免了原型對(duì)象共享的問(wèn)題。
  • 可以實(shí)現(xiàn)多繼承:由于JavaScript中可以在一個(gè)構(gòu)造函數(shù)中調(diào)用多個(gè)構(gòu)造函數(shù),因此可以通過(guò)借用構(gòu)造函數(shù)繼承來(lái)實(shí)現(xiàn)多繼承。

缺點(diǎn):

  • 無(wú)法重用父類(lèi)方法:由于借用構(gòu)造函數(shù)繼承創(chuàng)建了一個(gè)新的對(duì)象,因此無(wú)法重用父類(lèi)的方法。
  • 父類(lèi)方法更新不會(huì)自動(dòng)同步到子類(lèi)實(shí)例:由于借用構(gòu)造函數(shù)繼承創(chuàng)建了一個(gè)新的對(duì)象,因此父類(lèi)的方法更新不會(huì)自動(dòng)同步到子類(lèi)實(shí)例中。
  • 無(wú)法訪(fǎng)問(wèn)父類(lèi)原型上的屬性和方法:由于借用構(gòu)造函數(shù)繼承只繼承了父類(lèi)的實(shí)例屬性和方法,因此無(wú)法訪(fǎng)問(wèn)父類(lèi)原型上的屬性和方法。如果需要訪(fǎng)問(wèn)父類(lèi)原型上的屬性和方法,仍然需要通過(guò)將子類(lèi)的原型指向父類(lèi)的實(shí)例來(lái)實(shí)現(xiàn)。

3. 組合繼承

JavaScript中的組合繼承是一種結(jié)合借用構(gòu)造函數(shù)和原型鏈繼承的方式,它的核心思想是使用借用構(gòu)造函數(shù)繼承實(shí)例屬性和方法,使用原型鏈繼承共享屬性和方法。

以下是一個(gè)組合繼承的示例代碼:

functionAnimal(name) {
  this.name = name;
}
 
Animal.prototype.sayName = function() {
  console.log('My name is ' + this.name);
}
 
functionDog(name, age) {
  Animal.call(this, name); // 借用Animal構(gòu)造函數(shù),并將this指向Dog實(shí)例this.age = age;
}
 
Dog.prototype = newAnimal(); // 原型鏈繼承Animal類(lèi)的屬性和方法Dog.prototype.constructor = Dog; // 修復(fù)構(gòu)造函數(shù)指向let dog1 = newDog('旺財(cái)', 2);
let dog2 = newDog('小白', 1);
 
console.log(dog1.name); // '旺財(cái)'console.log(dog2.age); // 1
dog1.sayName(); // 'My name is 旺財(cái)'復(fù)制代碼

在上面的代碼中,Dog類(lèi)通過(guò)借用Animal構(gòu)造函數(shù)繼承實(shí)例屬性和方法,通過(guò)原型鏈繼承Animal類(lèi)的屬性和方法。由于子類(lèi)實(shí)例與父類(lèi)實(shí)例之間不存在原型鏈的關(guān)系,因此修改一個(gè)實(shí)例的屬性不會(huì)影響到其他實(shí)例。同時(shí),子類(lèi)實(shí)例可以重用父類(lèi)原型對(duì)象上的方法。

需要注意的是,由于在上面的代碼中通過(guò)Dog.prototype = new Animal()創(chuàng)建了一個(gè)新的Animal實(shí)例,因此在創(chuàng)建Dog類(lèi)時(shí)會(huì)調(diào)用兩次Animal構(gòu)造函數(shù),造成了性能上的浪費(fèi)。可以使用寄生組合式繼承來(lái)解決這個(gè)問(wèn)題。

具體來(lái)說(shuō),組合繼承通過(guò)將父類(lèi)的構(gòu)造函數(shù)借用到子類(lèi)中,從而實(shí)現(xiàn)了父類(lèi)屬性的繼承,同時(shí)通過(guò)將子類(lèi)的原型設(shè)置為一個(gè)新的父類(lèi)實(shí)例,從而實(shí)現(xiàn)了父類(lèi)方法的繼承。這種繼承方式具有以下優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 父類(lèi)的構(gòu)造函數(shù)可以傳遞參數(shù),并且不會(huì)影響到其他實(shí)例。
  • 子類(lèi)實(shí)例可以訪(fǎng)問(wèn)父類(lèi)原型對(duì)象上的方法,可以重用父類(lèi)的方法。
  • 可以實(shí)現(xiàn)多繼承。
  • 實(shí)現(xiàn)簡(jiǎn)單、易于理解。

缺點(diǎn):

  • 子類(lèi)實(shí)例會(huì)同時(shí)擁有自己的屬性和方法,以及父類(lèi)的屬性和方法,可能導(dǎo)致內(nèi)存浪費(fèi)和屬性名沖突的問(wèn)題。
  • 在創(chuàng)建子類(lèi)實(shí)例時(shí),父類(lèi)構(gòu)造函數(shù)會(huì)被調(diào)用兩次,可能會(huì)影響性能。

4. 原型式繼承

JavaScript中的原型式繼承是一種基于已有對(duì)象創(chuàng)建新對(duì)象的繼承方式,它利用了對(duì)象的動(dòng)態(tài)特性,通過(guò)封裝一個(gè)函數(shù)來(lái)實(shí)現(xiàn)繼承。該函數(shù)接收一個(gè)用作新對(duì)象原型的對(duì)象作為參數(shù),并返回一個(gè)新對(duì)象,從而實(shí)現(xiàn)了繼承。該方式與借用構(gòu)造函數(shù)繼承類(lèi)似,但它并不涉及到構(gòu)造函數(shù)和實(shí)例的概念。原型式繼承具有以下特點(diǎn):

基于已有對(duì)象創(chuàng)建新對(duì)象。

可以使用Object.create()方法實(shí)現(xiàn)。

可以將一個(gè)對(duì)象作為另一個(gè)對(duì)象的原型對(duì)象。

可以使用原型對(duì)象的屬性和方法,但不會(huì)影響到原型對(duì)象本身。

下面是一個(gè)使用原型式繼承的示例代碼:

let animal = {
  type: 'animal',
  sayType: function() {
    console.log('I am a ' + this.type);
  }
};
 
let dog = Object.create(animal); // 使用animal對(duì)象作為dog對(duì)象的原型
dog.type = 'dog';
 
dog.sayType(); // 'I am a dog'復(fù)制代碼

在上面的代碼中,animal對(duì)象擁有一個(gè)type屬性和一個(gè)sayType方法,dog對(duì)象通過(guò)使用animal對(duì)象作為原型對(duì)象來(lái)實(shí)現(xiàn)了繼承。因此,dog對(duì)象可以使用原型對(duì)象的屬性和方法,但并不會(huì)影響到原型對(duì)象本身。此外,通過(guò)給dog對(duì)象添加一個(gè)type屬性,也可以覆蓋原型對(duì)象的type屬性,實(shí)現(xiàn)對(duì)父對(duì)象屬性的重寫(xiě)。原型式繼承的優(yōu)點(diǎn)在于可以方便地實(shí)現(xiàn)對(duì)象的復(fù)用,但也容易導(dǎo)致對(duì)象之間的耦合,不易于維護(hù)。

具體來(lái)說(shuō),它通過(guò)創(chuàng)建一個(gè)空對(duì)象,并將其原型設(shè)置為一個(gè)已有對(duì)象,然后向這個(gè)空對(duì)象中添加屬性和方法來(lái)實(shí)現(xiàn)繼承。原型式繼承具有以下優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 簡(jiǎn)單、易于理解和實(shí)現(xiàn)。
  • 可以基于一個(gè)對(duì)象創(chuàng)建多個(gè)對(duì)象,實(shí)現(xiàn)對(duì)象復(fù)用。

缺點(diǎn):

  • 父對(duì)象的引用屬性會(huì)被所有子對(duì)象共享,因此子對(duì)象的修改會(huì)影響到其他子對(duì)象。
  • 子對(duì)象無(wú)法像傳統(tǒng)的類(lèi)繼承一樣判斷自己是否是父對(duì)象的實(shí)例。
  • 無(wú)法實(shí)現(xiàn)多繼承。

5. 寄生式繼承

JavaScript中的寄生式繼承是一種基于已有對(duì)象創(chuàng)建新對(duì)象的繼承方式,類(lèi)似于原型式繼承。它的主要區(qū)別是,在新創(chuàng)建的對(duì)象上增加一個(gè)方法,而這個(gè)方法的作用是以某種方式增強(qiáng)對(duì)象,然后返回這個(gè)對(duì)象。這種繼承方式得名于“寄生”,因?yàn)樵鰪?qiáng)對(duì)象的方法通常是基于已有的對(duì)象進(jìn)行“寄生”而得名。

寄生式繼承的優(yōu)點(diǎn)是可以封裝繼承過(guò)程,并且可以向?qū)ο笾刑砑右恍╊~外的屬性和方法。但是和原型式繼承一樣,也存在父對(duì)象的引用屬性被所有子對(duì)象共享、無(wú)法判斷實(shí)例是否是父對(duì)象的實(shí)例等問(wèn)題。

以下是一個(gè)使用寄生式繼承的示例代碼:

functioncreateAnimal(type) {
  let animal = {
    type: type,
    sayType: function() {
      console.log('I am a ' + this.type);
    }
  };
  // 基于animal對(duì)象進(jìn)行寄生增強(qiáng)let dog = Object.create(animal);
  dog.bark = function() {
    console.log('woof woof');
  };
  return dog;
}
 
let myDog = createAnimal('canine');
myDog.sayType(); // 'I am a canine'
myDog.bark(); // 'woof woof'復(fù)制代碼

在上面的代碼中,我們定義了一個(gè)名為createAnimal的函數(shù),用于創(chuàng)建一個(gè)繼承自animal對(duì)象的新對(duì)象。我們?cè)谶@個(gè)新對(duì)象上增加了一個(gè)bark方法,用于讓對(duì)象發(fā)出叫聲。最后,我們返回這個(gè)新對(duì)象,并將它賦值給myDog變量。通過(guò)這樣的方式,我們成功地實(shí)現(xiàn)了寄生式繼承。

具體來(lái)說(shuō),它在原型式繼承的基礎(chǔ)上增加了一個(gè)包裝函數(shù),該函數(shù)用于封裝繼承過(guò)程中的一些增強(qiáng)行為。寄生式繼承具有以下優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 簡(jiǎn)單、易于理解和實(shí)現(xiàn)。
  • 可以基于一個(gè)對(duì)象創(chuàng)建多個(gè)對(duì)象,實(shí)現(xiàn)對(duì)象復(fù)用。
  • 可以在不修改原對(duì)象的情況下,對(duì)繼承過(guò)程進(jìn)行一些增強(qiáng),例如添加新的屬性和方法。

缺點(diǎn):

  • 父對(duì)象的引用屬性會(huì)被所有子對(duì)象共享,因此子對(duì)象的修改會(huì)影響到其他子對(duì)象。
  • 子對(duì)象無(wú)法像傳統(tǒng)的類(lèi)繼承一樣判斷自己是否是父對(duì)象的實(shí)例。
  • 增強(qiáng)行為可能會(huì)帶來(lái)一定的性能開(kāi)銷(xiāo)。
  • 可能會(huì)導(dǎo)致代碼的可讀性降低。

6. 寄生式組合繼承

JavaScript中的寄生式組合繼承是一種結(jié)合了組合繼承和寄生式繼承的繼承方式。具體來(lái)說(shuō),它在組合繼承的基礎(chǔ)上,通過(guò)寄生式繼承來(lái)解決組合繼承中重復(fù)調(diào)用父構(gòu)造函數(shù)的問(wèn)題。

下面是一個(gè)使用寄生式組合繼承的示例代碼:

functionAnimal(name) {
  this.name = name;
  this.type = 'mammal';
}
 
Animal.prototype.sayName = function() {
  console.log('My name is ' + this.name);
};
 
functionDog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
 
// 使用寄生式繼承繼承Animal.prototypeDog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
 
Dog.prototype.sayBreed = function() {
  console.log('I am a ' + this.breed);
};
 
let myDog = newDog('Max', 'Golden Retriever');
myDog.sayName(); // 'My name is Max'
myDog.sayBreed(); // 'I am a Golden Retriever'復(fù)制代碼

在上面的代碼中,我們定義了Animal和Dog兩個(gè)構(gòu)造函數(shù),其中Animal構(gòu)造函數(shù)定義了一個(gè)name屬性和一個(gè)sayName()方法,Dog構(gòu)造函數(shù)在A(yíng)nimal的基礎(chǔ)上添加了一個(gè)breed屬性和一個(gè)sayBreed()方法。為了實(shí)現(xiàn)寄生式組合繼承,我們使用Object.create()方法基于A(yíng)nimal.prototype創(chuàng)建了一個(gè)新的對(duì)象,并將其賦值給Dog.prototype,從而使得Dog.prototype的原型鏈指向了Animal.prototype。同時(shí),我們還將Dog.prototype的constructor屬性設(shè)置為Dog,以保證繼承鏈的完整性。最后,我們通過(guò)調(diào)用Animal構(gòu)造函數(shù)并將this指向Dog對(duì)象,實(shí)現(xiàn)了對(duì)Animal屬性的繼承。通過(guò)這種方式,我們既避免了組合繼承中重復(fù)調(diào)用父構(gòu)造函數(shù)的問(wèn)題,又保留了寄生式繼承的靈活性,實(shí)現(xiàn)了一個(gè)高效而且靈活的繼承方式。

優(yōu)點(diǎn):

  • 實(shí)現(xiàn)了屬性和方法的完整繼承。
  • 避免了組合繼承中重復(fù)調(diào)用父類(lèi)構(gòu)造函數(shù)的問(wèn)題,提高了性能。
  • 可以在不修改原對(duì)象的情況下,對(duì)繼承過(guò)程進(jìn)行一些增強(qiáng),例如添加新的屬性和方法。

缺點(diǎn):

  • 增加了一層包裝函數(shù),可能會(huì)帶來(lái)一定的性能開(kāi)銷(xiāo)。
  • 可能會(huì)導(dǎo)致代碼的可讀性降低。

7. class繼承

在ES6及以上的版本中,JavaScript引入了class關(guān)鍵字,用于定義類(lèi),從而實(shí)現(xiàn)面向?qū)ο缶幊獭lass繼承是一種通過(guò)類(lèi)來(lái)實(shí)現(xiàn)繼承的方式,它使用extends關(guān)鍵字來(lái)指定父類(lèi),并通過(guò)super關(guān)鍵字來(lái)調(diào)用父類(lèi)的構(gòu)造函數(shù)和方法。

以下是一個(gè)使用class繼承的示例代碼:

classAnimal {
  constructor(type) {
    this.type = type;
  }
 
  sayType() {
    console.log('I am a ' + this.type);
  }
}
 
classDogextendsAnimal {
  constructor(type, name) {
    super(type);
    this.name = name;
  }
 
  sayName() {
    console.log('My name is ' + this.name);
  }
}
 
let dog = newDog('canine', 'Fido'); // 創(chuàng)建一個(gè)新的Dog對(duì)象
 
dog.sayType(); // 'I am a canine'
dog.sayName(); // 'My name is Fido'復(fù)制代碼

在上面的代碼中,我們首先定義了一個(gè)Animal類(lèi),它包含一個(gè)構(gòu)造函數(shù)和一個(gè)sayType()方法。然后我們通過(guò)extends關(guān)鍵字來(lái)指定Dog類(lèi)的父類(lèi)為Animal,并在Dog類(lèi)的構(gòu)造函數(shù)中通過(guò)super關(guān)鍵字來(lái)調(diào)用Animal構(gòu)造函數(shù),并實(shí)現(xiàn)了sayName()方法。最后,我們創(chuàng)建一個(gè)新的Dog對(duì)象,并調(diào)用它的方法來(lái)測(cè)試?yán)^承是否成功。

優(yōu)點(diǎn):

  • 代碼可讀性高,更易于理解和維護(hù)。
  • 語(yǔ)法簡(jiǎn)潔,可以更快地編寫(xiě)代碼。
  • 可以使用現(xiàn)代JavaScript特性,如箭頭函數(shù)、解構(gòu)賦值等。

缺點(diǎn):

  • 與ES5及以下版本的JavaScript不兼容。
  • 需要編譯才能運(yùn)行在低版本瀏覽器中。
  • 某些開(kāi)發(fā)者可能認(rèn)為使用類(lèi)和繼承違背了JavaScript的本質(zhì)。

總體來(lái)說(shuō),class繼承是一種非常方便的繼承方式,特別是在面向?qū)ο缶幊讨?,能夠大大?jiǎn)化代碼的編寫(xiě)和維護(hù)。但在一些特定情況下,其他繼承方式可能更為適合。

以上就是JavaScript中常見(jiàn)的七種繼承及實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript繼承的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論