JS中的多種繼承方式

JS自己是基於面向對象開發的編程語言:因此咱們學習JS主要就是學習類,以及類的實例,在學習類的原型上提供的方法;javascript

類:java

  • 封裝、繼承、多態

1、類的封裝

  • 類也是一個函數,把實現一個功能的代碼進行封裝,以此實現「低耦合高內聚」

2、多態:重載、重寫

  • 重寫:子類重寫父類上的方法(伴隨着繼承運行的)
  • 重載:相同的方法,因爲參數或者返回值不一樣,具有了不一樣的功能(JS中不具有嚴格意義上的重載,JS中的重載:同一個方法內,根據傳參不一樣實現不一樣的功能)

一、在後臺語言當中:

  • 重載定義:
    • 指的是在方法名字相同,都叫fn,可是參數的類型或者參數的個數不一樣,因此致使這兩個方法不是一個方法,而變成了兩個方法;(咱們認爲這就是方法的重載)
  • 重載的目的:(爲了分擔壓力的)
    • 正常狀況下,客戶端向服務器發請求,一臺服務器容許多個客戶端同時發請求,因此服務器要有很強的抗壓能力,
    • 若是說咱們把全部的功能(好比傳兩個參數幹什麼,傳一個參數幹什麼)都放在一個方法中,那這個方法承載的壓力就會比較大,
    • 因此咱們通常經過重載的方式來分發出這個方法承載的壓力;
//=> 在後臺語言中,
public void fn(int x,init y){

}
public void fn(int x){

}
fn(10,20);  執行第一個FN
fn(10);  執行第二個FN
fn('小芝麻');  報錯  

複製代碼

二、在JS語言中:這種方式並非重載

  • 因爲JS中的變量提高機制,因此只執行第二個
  • 因此說JS中不具有嚴格意義上的重載,
function fn(x, y) {

}

function fn(x) {

}
fn(10, 20);  執行第二個FN
fn(10); 執行第二個FN 
複製代碼
  • JS中的重載:同一個方法內,根據傳參不一樣實現不一樣的功能
function fn(x, y) {
    if(y===undefined){
        // ...
        return;
    }
    // ....
}
fn(10);
fn(10, 20); 
複製代碼

3、繼承:子類繼承父類中的方法

這裏想到了一個笑話:編程

A去醫院檢查,被查出是「類風溼性關節炎」
A:好鬱悶,得了…
B:這個病注意點就行了啊
A:不行,由於咱們打算要孩子
B:你這個病和孩子有啥關係啊
….
10min  A的腦子中 "類風溼性關節炎」 => 類是繼承的 複製代碼

在生物學上的繼承相似於:瀏覽器

父親:基因
   AAA  BBB  CCC

兒子:繼承了父親的基因(把父親的部分基因,直接拷貝到自身上)
   AAA  BBB 
   兒子基因突變  AAB  BBA,可是對父親是沒有影響的
複製代碼

正常的後臺語言中的繼承,就是這種拷貝式的繼承,可是咱們JS中的繼承並非這種繼承;bash

一、繼承的定義:

  • 在JS語言中,它的繼承和其它編程語言仍是不太同樣的

二、繼承的目的:

  • 讓子類的實例同時也具有父類中私有的屬性和公共的方法

三、JS中繼承方案

第一種:原型繼承(讓子類的原型等於父類的實例便可)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	this.y = 200;
}
//=> 讓子類的原型等於父類的實例
Child.prototype = new Parent; //=>原型繼承

Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 
複製代碼

如今c1 能用的方法是:私有的 y 和公有的 getY方法,以及Object上的公有方法;服務器

1.原理

  • 子類的實例,可以用子類私有的和原型上公有的
  • 父類的實例,可使用 父類私有和原型上公有的

因此咱們只要讓子類的原型等於父類的實例便可編程語言

  • Child.prototype = new Parent

2.特色

  • 一、父類中私有和公有的屬性方法,最後都變爲子類實例公有的
  • 二、和其餘語言不一樣的是
    • 原型繼承並不會把父類的屬性方法「拷貝」給子類,
    • 而是讓子類實例基於__proto__原型鏈找到本身定義的屬性和方法「指向/查找」方式的

3.優缺點

  • c1.__proto__.xxx = xxx 修改子類原型(原有父類的一個實例)中的內容,內容被修改後,對子類的其餘實例有影響,可是對父類的實例不會有影響函數

  • c1.__proto__.__proto__.xxx = xxx直接修改的是父類原型,這樣不只會影響其它父類的實例,也影響其餘子類的實例學習

  • JS中的重寫影響很大ui

第二種:CALL繼承(只能繼承父類中私有的,不能繼承父類中公共的)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	// 在子類構造函數中,把父類當作普通方法執行(沒有父類實例,父類原型上的那些東西也就和它不要緊了)
	// this -> Child的實例c1
	Parent.call(this); // this.x=100 至關於強制給c1這個實例設置一個私有的屬性x,屬性值100,至關於讓子類的實例繼承了父類的私有的屬性,而且也變爲了子類私有的屬性 「拷貝式」
	this.y = 200;
}
Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 
複製代碼

1.原理

  • 在子類構造函數中,把父類看成普通函數執行(沒有父類實例,父類原型上的那些東西也就和他沒有關係了)
    • 此時 parent(this) 這個 thiswindow
  • 經過 call 強制改變 this 的指向爲 Child 中的 this
    • Parent.call(this)
    • 此時 this 是當前 Child 中的 this

至關於強制給實例設置了一個私有的屬性,至關於讓子類的實例繼承了父類的私有的屬性,而且也變爲了子類私有的屬性「拷貝式」

2.特色

  • 只能繼承父類中私有的,公有的不行(而且是把父類私有的變成子類私有的)

咱們滿意的繼承方式應該是:父類私有變爲子類私有 父類公有變爲子類公有

第三種:寄生組合式繼承(CALL繼承 + 另類原型繼承)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	Parent.call(this);
	this.y = 200;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 
複製代碼
  • 上面咱們說了 call 繼承有個好處:能把父類私有的變成子類私有的;因此 call 繼承咱們留下來

那如今咱們只要想辦法讓父類中公有的也能變成子類中公有的便可;

1.原理

咱們剛纔的原型繼承,大致的實現了這個功能;

  • 原型繼承是建立了父類的一個實例,從而達到子類繼承父類的效果;它最終實現的核心點是:Child.prototype.__proto__ === Parent.prototype;
  • 若是說咱們在原型繼承時不想要父類私有的內容,咱們只須要:Child.prototype.__proto__ = Parent.prototype;

這樣雖然能實現效果可是IE瀏覽器中不容許咱們操做__proto__,那咱們有什麼能夠替換它呢?

// Object.create();建立一個空對象,讓其原型鏈指向obj
let obj = {
	xxx: 'xxx'
};
console.log(Object.create(obj));
複製代碼

因此能夠寫成:Child.prototype = Object.create(Parent.prototype);便可

2.缺點:

  • 因爲新建立的原型沒有constructor屬性,因此咱們本身手動加一個默認的;

4、ES6中的類和繼承

  • class類
class Parent {
	constructor() {
		this.x = 100;
	}
	// Parent.prototype.getX=function...
	getX() {
		return this.x;
	}
}
複製代碼
  • 繼承
// 繼承: extends Parent(相似於寄生組合繼承)
// 注意:繼承後必定要在CONSTRUCTOR第一行加上SUPER
class Child extends Parent {
	constructor() {
		super(); //=>相似於咱們以前的CALL繼承 super(100,200):至關於把Parent中的constructor執行,傳遞了100和200
		this.y = 200;
	}
	getY() {
		return this.y;
	}
}
let c1 = new Child;
console.log(c1);

// Child(); //=>Uncaught TypeError: Class constructor Child cannot be invoked without 'new' ES6中建立的就是類,不能當作普通函數執行,只能new執行
複製代碼

思惟導圖

相關文章
相關標籤/搜索