ES6+ Class
1. 前言
上一節(jié)我們主要回顧了在 ES5 中使用構(gòu)造函數(shù)的方式實(shí)現(xiàn)類(lèi)了,并說(shuō)明了如何實(shí)現(xiàn)類(lèi)的繼承。從上一節(jié)的講解中,構(gòu)造函數(shù)去實(shí)現(xiàn)類(lèi)的繼承還是有諸多繁瑣的地方的,我們需要考慮子類(lèi)和父類(lèi)的關(guān)系,繼承中的細(xì)節(jié)都需要自己手動(dòng)處理。本節(jié)我們將要學(xué)習(xí) ES6 中的類(lèi)的基本使用和類(lèi)的繼承。在學(xué)習(xí)本節(jié)我們需要明確的是,ES6 中的類(lèi)是基于現(xiàn)有語(yǔ)法的原型實(shí)現(xiàn)的,并沒(méi)有引入新的面相對(duì)象的模型。它的本質(zhì)還是我們上節(jié)提到的構(gòu)造函數(shù),只是讓我們更加方便地使用,是基于原型的繼承的語(yǔ)法糖。
2. 基本用法
2.1 語(yǔ)法
上節(jié)我們?cè)趯?shí)現(xiàn)類(lèi)的時(shí)候說(shuō),類(lèi)不能被執(zhí)行只能 new 來(lái)創(chuàng)建實(shí)例,當(dāng)時(shí)我們是在內(nèi)部手動(dòng)處理的。在 ES6 中天然支持這個(gè)特性:
class Animal { }
Animal();
// Uncaught TypeError: Class constructor Animal cannot be invoked without 'new'
上面的代碼中我們定義了一個(gè)動(dòng)物類(lèi),在控制臺(tái)中讓其執(zhí)行,會(huì)看到如上的未捕獲的類(lèi)型錯(cuò)誤:意思是在沒(méi)有 new 的情況下是無(wú)法調(diào)用構(gòu)造函數(shù) Animal 的。使用 class 定義的類(lèi)和使用構(gòu)造函數(shù)定義類(lèi),在使用上是一樣的,只是創(chuàng)建類(lèi)的方式不一樣。
上節(jié)我們知道如何創(chuàng)建實(shí)例上的屬性和原型上的屬性,那么使用 class 是怎么實(shí)現(xiàn)的呢?class 類(lèi)提供了 constructor 方法,并在 new 的時(shí)候默認(rèn)執(zhí)行,所以在 constructor 函數(shù)內(nèi)部在 this 上綁定實(shí)例上的屬性。而 原型上的方法則是在對(duì)象中直接添加屬性即可,實(shí)例如下:
class Animal {
constructor() {
this.type = '鳥(niǎo)類(lèi)'
}
eat() {}
}
var a = new Animal();
console.log(a.hasOwnProperty('type')); // true
console.log(a.hasOwnProperty('eat')); // false
另外,在 ES7 中 class 還提供了一種方式在實(shí)例上綁定屬性,這種方式不需要 this,直接使用等號(hào)在 class 類(lèi)中進(jìn)行賦值。
class Animal {
constructor() {
this.type = '鳥(niǎo)類(lèi)'
}
age='100'
eat() {}
}
var a = new Animal();
console.log(a.hasOwnProperty('age')); // true
需要注意的是,上面的等號(hào)賦值方式要在支持 ES7 的環(huán)境中才能執(zhí)行。
2.2 get/set
當(dāng)我們深入了解對(duì)象時(shí)我們就會(huì)知道屬性的 getter 和 setter ,提供了 get 和 set 兩個(gè)方法用于訪問(wèn)和設(shè)置屬性。在 ES5 中有 Object.defineProperty()
方法可以對(duì)對(duì)象的屬性進(jìn)行劫持,Vue2 的底層就是使用這個(gè) API 實(shí)現(xiàn)的。當(dāng)然 class 類(lèi)其實(shí)也是一個(gè)對(duì)象,它也可以使用 get 的方式返回屬性值。如下實(shí)例:
class Animal {
constructor() {
this.type = "鳥(niǎo)類(lèi)";
this._age = 8;
}
get a() {
return this._age;
}
set a(newValue) {
this._age = newValue;
}
}
var animal = new Animal();
console.log(animal.a); // 8
animal.a = 10;
console.log(animal.a); // 10
上面代碼中我們就使用了 get 和 set 去獲取屬性值和設(shè)置屬性值。那我們思考一個(gè)問(wèn)題,set 和 get 是自有屬性還是原型上的屬性呢?其實(shí) get 和 set 還是 class 類(lèi)上的一個(gè)方法,所以是原型上的方法。
console.log(a.hasOwnProperty('a')); // false
2.3 static
ES6 提供了用于定義靜態(tài)屬性和方法的關(guān)鍵字 static
,靜態(tài)方法調(diào)用時(shí)不需要實(shí)例化該類(lèi),所以就不能通過(guò)實(shí)例去調(diào)用,但可以使用類(lèi)直接去調(diào)用。
靜態(tài)方法通常用于為一個(gè)應(yīng)用程序創(chuàng)建工具函數(shù),下面我們來(lái)看一個(gè)長(zhǎng)方形類(lèi),定義一個(gè)獲取長(zhǎng)方形面積的靜態(tài)方法。
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
static getArea(r) {
return r.width * r.height;
}
}
const r = new Rectangle(5, 10);
console.log(Rectangle.getArea(r)); // 50
3. 繼承
3.1 extends
在上節(jié)構(gòu)造函數(shù)中的繼承我們知道,子類(lèi)的構(gòu)造函數(shù)中,需要我們?nèi)ナ謩?dòng)執(zhí)行父構(gòu)造函數(shù)并綁定this,還需要將子類(lèi)的構(gòu)造函數(shù)的原型鏈執(zhí)行父類(lèi)的原型。ES6 中的繼承非常簡(jiǎn)單,在創(chuàng)建子類(lèi)時(shí)只需要使用關(guān)鍵字 extends
即可創(chuàng)建一個(gè)子類(lèi)。
// 父類(lèi):動(dòng)物
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(this.name + '會(huì)吃飯!');
}
static getAge() {
console.log('獲取' + this.name + '的年齡10歲了');
return 10;
}
}
// 子類(lèi):具體的動(dòng)物——狗
class Dog extends Animal {}
上面的代碼中子類(lèi) Owl 繼承了 Animal,那這個(gè)時(shí)候我們都繼承了什么呢?從上面的學(xué)習(xí)中父類(lèi)中有,this 上的屬性,原型上的方法和靜態(tài)方法。
var dog = new Dog('狗');
console.log('name:', dog.name); // name: 狗
console.log('age:', Dog.getAge()); // age: 10
dog.eat(); // 狗會(huì)吃飯!
從上面代碼打印的結(jié)果,我們知道,實(shí)例 dog 已經(jīng)繼承了 Animal 上的屬性和方法。在父類(lèi)中對(duì) eat 方法的定義不明確,所以在子類(lèi)中我們重寫(xiě) eat 方法。
class Dog extends Animal {
eat() {
console.log(this.name + '會(huì)吃飯!');
}
}
var dog = new Dog('狗');
dog.eat(); // 狗喜歡吃骨頭!
3.2 super
super
是 class 中的關(guān)鍵字,可以理解是父類(lèi)的別名,用于調(diào)用對(duì)象的父對(duì)象上的函數(shù)。一般 super
有兩種情況:super
當(dāng)做函數(shù)調(diào)用;一種是 super
當(dāng)做父類(lèi)的對(duì)象使用。
第一種情況下,super
關(guān)鍵字作為函數(shù)調(diào)用,它的作用是為了綁定 this。所以子類(lèi)的構(gòu)造函數(shù)必須執(zhí)行一次 super。默認(rèn)情況下,類(lèi)中不寫(xiě) constructor 時(shí),constructor 會(huì)自動(dòng)執(zhí)行 super, 并綁定 this 指向當(dāng)前子類(lèi)。
class A {}
class B extends A {
constructor() {
super();
}
}
上節(jié)中我們?cè)趧?chuàng)建子類(lèi)時(shí)就去執(zhí)行了父類(lèi)并綁定了this,上面代碼中的 super 和 A.call(this)
是相同的。
第二種情況下,super 當(dāng)作父類(lèi)的對(duì)象來(lái)使用的,什么情況下會(huì)使用呢?當(dāng)我們?cè)谧宇?lèi)中想使用父類(lèi)的方法時(shí)可以使用 super 直接調(diào)用父類(lèi)的方法即可。
class A {
getCount() {
return 7;
}
}
class B extends A {
constructor() {
super();
console.log(super.getCount()); // 7
}
}
let b = new B();
4. 小結(jié)
本節(jié)主要學(xué)習(xí)了 ES6 中 class 類(lèi)的使用和相關(guān)的知識(shí)點(diǎn),需要明確的是 class 類(lèi)就是一個(gè)語(yǔ)法糖,底層還是基于現(xiàn)有的原型對(duì)象的繼承來(lái)實(shí)現(xiàn)的。所以要想深入理解 ES6 的 class 就需要對(duì) ES5 中的構(gòu)造函數(shù)有深入的理解,另外,我們可以使用 babel 進(jìn)行轉(zhuǎn)譯,得到的代碼就是使用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)的。