JS本身是基於物件導向開發的程式語言:所以我們學習JS主要就是學習類,以及類的例項,在學習類的原型上提供的方法;
類:
- 封裝、繼承、多型
一、類的封裝
- 類也是一個函式,把實現一個功能的程式碼進行封裝,以此實現“低耦合高內聚”
二、多型:過載、重寫
- 重寫:子類重寫父類上的方法(伴隨著繼承執行的)
- 過載:相同的方法,由於引數或者返回值不同,具備了不同的功能(JS中不具備嚴格意義上的過載,JS中的過載:同一個方法內,根據傳參不同實現不同的功能)
1、在後臺語言當中:
- 過載定義:
- 指的是在方法名字相同,都叫fn,但是引數的型別或者引數的個數不同,所以導致這兩個方法不是一個方法,而變成了兩個方法;(我們認為這就是方法的過載)
- 過載的目的:(為了分擔壓力的)
- 正常情況下,客戶端向伺服器發請求,一臺伺服器允許多個客戶端同時發請求,所以伺服器要有很強的抗壓能力,
- 如果說我們把所有的功能(比如傳兩個引數幹什麼,傳一個引數幹什麼)都放在一個方法中,那這個方法承載的壓力就會比較大,
- 所以我們一般通過過載的方式來分發出這個方法承載的壓力;
//=> 在後臺語言中,
public void fn(int x,init y){
}
public void fn(int x){
}
fn(10,20); 執行第一個FN
fn(10); 執行第二個FN
fn('小芝麻'); 報錯
複製程式碼
2、在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);
複製程式碼
三、繼承:子類繼承父類中的方法
這裡想到了一個笑話:
A去醫院檢查,被查出是“類風溼性關節炎”
A:好鬱悶,得了…
B:這個病注意點就好了啊
A:不行,因為我們打算要孩子
B:你這個病和孩子有啥關係啊
….
10min A的腦子中 "類風溼性關節炎” => 類是繼承的
複製程式碼
在生物學上的繼承類似於:
父親:基因
AAA BBB CCC
兒子:繼承了父親的基因(把父親的部分基因,直接拷貝到自身上)
AAA BBB
兒子基因突變 AAB BBA,但是對父親是沒有影響的
複製程式碼
正常的後臺語言中的繼承,就是這種拷貝式的繼承,但是我們JS中的繼承並不是這種繼承;
1、繼承的定義:
- 在JS語言中,它的繼承和其它程式語言還是不太一樣的
2、繼承的目的:
- 讓子類的例項同時也具備父類中私有的屬性和公共的方法
3、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.特點
- 1、父類中私有和公有的屬性方法,最後都變為子類例項公有的
- 2、和其他語言不同的是
- 原型繼承並不會把父類的屬性方法“拷貝”給子類,
- 而是讓子類例項基於
__proto__
原型鏈找到自己定義的屬性和方法“指向/查詢”方式的
3.優缺點
-
c1.__proto__.xxx = xxx
修改子類原型(原有父類的一個例項)中的內容,內容被修改後,對子類的其他例項有影響,但是對父類的例項不會有影響 -
c1.__proto__.__proto__.xxx = xxx
直接修改的是父類原型,這樣不僅會影響其它父類的例項,也影響其他子類的例項 -
JS中的重寫影響很大
第二種: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)
這個this
是window
- 此時
- 通過
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
屬性,所以我們自己手動加一個預設的;
四、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執行
複製程式碼