首頁 >web前端 >js教程 >javascript實作繼承的方式有哪些

javascript實作繼承的方式有哪些

青灯夜游
青灯夜游原創
2021-06-22 16:21:109617瀏覽

javascript實作繼承的方式:1、原型鏈繼承;將父類別的實例當作子類別的原型。 2、建構繼承;使用父類別的建構子來增強子類別實例。 3、實例繼承;為父類別實例新增特性,以子類別實例傳回。 4、拷貝繼承。 5、組合繼承。 6.寄生組合繼承。

javascript實作繼承的方式有哪些

本教學操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。

JS作為物件導向的弱型別語言,繼承也是其非常強大的特性之一。那麼如何在JS中實現繼承呢?讓我們拭目以待。

JS繼承的實作方式

既然要實作繼承,那麼首先我們得有一個父類,程式碼如下:

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};

1、原型鏈繼承

核心: 將父類別的實例作為子類別的原型

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

特點:

  • 非常純粹的繼承關係,實例是子類別的實例,也是父類別的實例

  • 父類別新增原型方法/原型屬性,子類別都能存取到

  • #簡單,易於實作

缺點:

  • #要想為子類別新增屬性與方法,可以在Cat建構函數中,為Cat實例增加實例屬性。如果要新增原型屬性和方法,則必須放在new Animal()這樣的語句之後執行。

  • 無法實作多重繼承

  • 來自原型物件的所有屬性被所有實例共用

  • 建立子類別實例時,無法向父類別建構子傳參

2、建構繼承

核心:使用父類別的建構子來增強子類別實例,等於複製父類別的實例屬性給子類別(沒用到原型)

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特點:

  • ##解決了1中,當子類別實例共享父類別引用屬性的問題

  • 建立子類別實例時,可以向父類別傳遞參數

  • #可以實現多繼承(call多個父類別物件)

缺點:

  • 實例並不是父類別的實例,只是子類別的實例

  • 只能繼承父類別的實例屬性和方法,不能繼承原型屬性/方法

  • 無法實作函數重複使用,每個子類別都有父類別實例函數的副本,影響效能

3、實例繼承

#核心:為父類別實例新增特性,作為子類別實例傳回

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

特點:

  • 不限制呼叫方式,不管是

    new 子類別()還是子類別(),傳回的物件具有相同的效果

缺點:

  • 實例是父類別的實例,不是子類的實例

  • 不支援多重繼承

4、拷貝繼承

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  // 如下实现修改了原型对象,会导致单个实例修改name,会影响所有实例的name值
  // Cat.prototype.name = name || 'Tom'; 错误的语句,下一句为正确的实现
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特點:

  • 支援多重繼承

缺點:

  • #效率較低,記憶體佔用高(因為要拷貝父類別的屬性)

  • 無法取得父類別不可列舉的方法(不可枚舉方法,不能使用for in 存取)

5 、組合繼承

核心:透過呼叫父類別構造,繼承父類別的屬性並保留傳參的優點,然後透過將父類別實例作為子類別原型,實現函數重複使用

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

// 组合继承也是需要修复构造函数指向的。

Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特點:

  • 彌補了方式2的缺陷,可以繼承實例屬性/方法,也可以繼承原型屬性/方法

  • 既是子類別的實例,也是父類別的實例

  • 不存在引用屬性共享問題

  • 可傳參

  • 函數可重複使用

#缺點:

  • 呼叫了兩次父類構造函數,產生了兩個實例(子類別實例將子類別原型上的那份屏蔽了)

#6、寄生組合繼承

##核心:

透過寄生方式,砍掉父類別的實例屬性,這樣,在呼叫兩次父類別的建構的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
Cat.prototype.constructor = Cat; // 需要修复下构造函数
特點:

    堪稱完美
  • #缺點:

##實作較為複雜
  • 附錄程式碼:

範例一:
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
  //实例引用属性
  this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();

var tom = new Cat('Tom');
var kissy = new Cat('Kissy');

console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []

tom.name = 'Tom-New Name';
tom.features.push('eat');

//针对父类实例值类型成员的更改,不影响
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//针对父类实例引用类型成员的更改,会通过影响其他子类实例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']

原因分析:

關鍵點:屬性尋找過程

執行tom.features.push,先找tom物件的實例屬性(找不到),

那麼去原型物件找,也就是Animal的實例。發現有,那麼就直接在這個物件的features屬性中插入值。

在console.log(kissy.features); 的時候。同上,kissy實例上沒有,那麼去原型上找。

剛好原型上有,就直接返回,但是注意,這個原型物件中features屬性值已經改變了。

【相關推薦:javascript學習教學

以上是javascript實作繼承的方式有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn