1. 程式人生 > >js繼承的原理、方式以及優缺點

js繼承的原理、方式以及優缺點

之前看js繼承問題,自己查閱資料,看到有一篇文章整理還可以,便在文章的基礎上又細化了一些。

JS作為面向物件的弱型別語言,繼承也是其非常強大的特性之一。那麼如何在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

特點:

  1. 非常純粹的繼承關係,例項是子類的例項,也是父類的例項
  2. 父類新增原型方法/原型屬性,子類都能訪問到
  3. 簡單,易於實現

缺點:

  1. 可以在Cat建構函式中,為Cat例項增加例項屬性。如果要新增原型屬性和方法,則必須放在new Animal()這樣的語句之後執行。
  2. 無法實現多繼承
  3. 來自原型物件的引用屬性是所有例項共享的(詳細請看附錄程式碼: 示例1
  4. 建立子類例項時,無法向父類建構函式傳參

推薦指數:★★(3、4兩大致命缺陷)

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. 解決了1中,子類例項共享父類引用屬性的問題
  2. 建立子類例項時,可以向父類傳遞引數
  3. 可以實現多繼承(call多個父類物件)

缺點:

  1. 例項並不是父類的例項,只是子類的例項
  2. 只能繼承父類的例項屬性和方法,不能繼承原型屬性/方法
  3. 無法實現函式複用,每個子類都有父類例項函式的副本,影響效能

推薦指數:★★(缺點3)

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

特點:

  1. 不限制呼叫方式,不管是new 子類()還是子類(),返回的物件具有相同的效果

缺點:

  1. 例項是父類的例項,不是子類的例項
  2. 不支援多繼承

推薦指數:★★

4、拷貝繼承

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.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. 支援多繼承

缺點:

  1. 效率較低,記憶體佔用高(因為要拷貝父類的屬性)
  2. 無法獲取父類不可列舉的方法(不可列舉方法,不能使用for in 訪問到)

推薦指數:★(缺點1)

5、組合繼承

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

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

// 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

特點:

  1. 彌補了方式2的缺陷,可以繼承例項屬性/方法,也可以繼承原型屬性/方法
  2. 既是子類的例項,也是父類的例項
  3. 不存在引用屬性共享問題
  4. 可傳參
  5. 函式可複用

缺點:

  1. 呼叫了兩次父類建構函式,生成了兩份例項(子類例項將子類原型上的那份遮蔽了)

推薦指數:★★★★(僅僅多消耗了一點記憶體)

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

特點:

  1. 堪稱完美

缺點:

  1. 實現較為複雜

推薦指數:★★★★(實現複雜,扣掉一顆星)

附錄程式碼:

示例一:

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屬性值已經變化了。

相關推薦

js繼承原理方式以及優缺點

之前看js繼承問題,自己查閱資料,看到有一篇文章整理還可以,便在文章的基礎上又細化了一些。 JS作為面向物件的弱型別語言,繼承也是其非常強大的特性之一。那麼如何在JS中實現繼承呢?讓我們拭目以待。 JS繼承的實現方式 js繼承的實現方法:原型鏈繼承、構造繼承、例項繼

JS資料型別判斷方式優缺點String常用方法

資料型別判斷方式及優缺點 JS中一共有七種資料型別: 一種引用型別——Object, 六種基本資料型別——Number、String、Boolean、Null、Undifined、Symbol(ES6新增) typeof(var):返回的是一個字串,但是對於複

線性規劃中的單純形法與內點法(原理步驟以及matlab實現)(三)

應用 最大化 round 並不是 兩個 生產 陰影 3.3 ima 在本系列的第三篇博客中,筆者討論對偶單純形法的相關理論和應用 2.3 Dual Simplex Method(對偶單純形法) Contents   2.3.1 對偶問題產生的原因   2.3.2 對偶問題的

JS繼承幾種方式

person fun argument per arguments 訪問 temp 共享 構造 1、原型繼承<script> function Person(name,age){ this.name = name; this.age = age; } Per

Javascript深入之創建對象的多種方式以及優缺點

丟失 創建對象 工廠 pre 使用 OS 不能 屬性和方法 一次 1.工廠模式 function createPerson(name) { var o = new Object(); o.name = name; o.getName = function() {

JS】----JS繼承的實現方式

https://www.cnblogs.com/humin/p/4556820.html JS繼承的實現方式 既然要實現繼承,那麼首先我們得有一個父類,程式碼如下: // 定義一個動物類 function Animal (name) { // 屬性 this.name = name || 'An

Js中localStoragesessionStorage以及Cookie介紹

---恢復內容開始---基本概念 Cookie Cookie 是小甜餅的意思。顧名思義,cookie 確實非常小,它的大小限制為4KB左右。它的主要用途有儲存登入資訊,比如你登入某個網站市場可以看到“記住密碼”,這通常就是通過在 Cookie 中存入一段辨別使用者身份的資料來實現的。 localStorage

java多執行緒(二):建立執行緒的三種方式以及優缺點總結

一、Java中建立執行緒主要有三種方式: 1、繼承Thread類建立執行緒類 步驟: (1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了執行緒要完成的任務。因此把run()方法稱為執行體。 (2)建立Thread子類的例項,即建立了執行緒物件。

C++函式引數傳遞的3種方式以及優缺點(轉)

寫函式時遇到給予函式的引數變數無法被修改的問題,轉自:https://blog.csdn.net/zhaoxun91/article/details/75417492 1 函式引數傳遞的3種方式比較 1.1 按值傳遞 #include <iostream> using names

資料倉庫系列——01.拉鍊表(原理設計以及在Hive中的實現)

0x00 前言 過了半年時間,對資料倉庫的理解又有了一些不同的認識,翻出來之前寫的關於拉鍊表的內容,稍作修改重新發出來。本篇將會談一談在資料倉庫中拉鍊表相關的內容,包括它的原理、設計、以及在我們大資料場景下的實現方式。 內容 全文由下面幾個部分組成: 先分享一下拉

TCP/IP原理基礎以及在Linux上的實現(轉)

導言:本篇作為理論基礎,將向我們講述TCP/IP的基本原理以及重要的協議細節,並在此基礎上介紹了TCP/IP在LINUX上的實現。 OSI參考模型及TCP/IP參考模型OSI模型(open system interconnection reference model)是

ORM的簡介概述以及優缺點

一、ORM簡介 物件關係對映(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向物件與關係資料庫存在的互不匹配的現象的技術。 簡單的說,ORM是通過使用描述物件和資料庫之間對映的元資料,將程式中的物件自動持久化到關係資料庫

傅立葉變換的原理意義以及如何用Matlab實現快速傅立葉變換

本帖最後由 xiaoliu 於 2011-7-28 21:00 編輯一、傅立葉變換的由來關於傅立葉變換,無論是書本還是在網上可以很容易找到關於傅立葉變換的描述,但是大都是些故弄玄虛的文章,太過抽象,盡是一些讓人看了就望而生畏的公式的羅列,讓人很難能夠從感性上得到理解,最近,

漫談資料倉庫之拉鍊表(原理設計以及在Hive中的實現)

0x00 前言 本文將會談一談在資料倉庫中拉鍊表相關的內容,包括它的原理、設計、以及在我們大資料場景下的實現方式。 全文由下面幾個部分組成: 先分享一下拉鍊表的用途、什麼是拉鍊表。 通過一些小的使用場景來對拉鍊表做近一步的闡釋,以及拉鍊表和常用的

js讀寫cookie方式以及中文亂碼問題

讀寫cookie是前端工程師在做專案時會經常使用的技術。cookie是瀏覽器提供的機制、是javascript的另一種機制,可以達到真正全域性變數的要求。 它將document 物件的cookie屬性提供給JavaScript。可以由JavaScript對其進行

redis單例主從模式sentinel以及叢集的配置方式優缺點對比

redis作為一種高效的快取框架,使用是非常廣泛的,在資料儲存上,在執行時其將資料儲存在記憶體中,以實現資料的高效讀寫,並且根據定製的持久化規則不同,其會不定期的將資料持久化到硬碟中。另外相較於其他的NoSql資料庫,redis提供了非常豐富的資料結構,如dict,s

Java執行緒池的使用方式,核心執行原理以及注意事項

Java執行緒池的使用方式,核心執行原理、以及注意事項 執行緒池的緣由 執行緒池的處理流程 執行緒池的使用(ThreadPoolExecutor) 執行緒池的注意事項 執行緒池的緣由 java中為了提高併發度,可以使用多執行緒共同

關於js在iframe中顯示與隱藏的控制方式(displayvisilibity以及hide()/show())

引子: 在專案開發過程中,使用iframe巢狀不同的頁面,通過選單來控制頁面的切換展示。最開始使用的是show()和hide()方法來控制頁面的展示與隱藏,但是遇到巢狀的頁面高度不固定, 當巢狀頁面過高會出現滾動條,如果拉下滾動條到某個位置,然後切換頁面,再切換到上個頁面時滾動條就自動到頂部而不是在之前下

聚類方法:DBSCAN演算法研究(1)--DBSCAN原理流程引數設定優缺點以及演算法

DBSCAN聚類演算法三部分: 1、        DBSCAN原理、流程、引數設定、優缺點以及演算法; 2、        matlab程式碼實現; 3、        C++程式碼實現及與matlab例項結果比較。 DBSCAN(Density-based

1.事件委託的原理以及優缺點 2. 手寫原生js實現事件代理,並要求相容瀏覽器

Q:事件的委託(代理 Delegated Events)的原理以及優缺點 A:委託(代理)事件是那些被繫結到父級元素的事件,但是隻有當滿足一定匹配條件時才會被挪。這是靠事件的冒泡機制來實現的, 優點是: (1)可以大量節省記憶體佔用,減少事件註冊,比如在table上