類:構造函數的語法糖
傳統的構造函數的問題
- 屬性和原型方法定義分離,降低了可讀性
- 原型成員可以被枚舉
- 默認情況下,構造函數仍然可以被當作普通函數使用
類的寫法
在es6的class中一個類的靜態方法,私有屬性,原型方法均寫在了一起,解決了傳統構造函數的屬性原型分離的問題
class Plane {
// 靜態屬性,相當於Plane.alive
static alive() {
return true
}
constructor(name) { // 這裏是參數
// 私有屬性
this.name = name || '普通飛機';
this.blood = 100;
}
// 原型方法 forin不可枚舉此方法
fly() {
console.log('fly')
}
}
類的特點
- 類聲明不會被提升,與 let 和 const 一樣,存在暫時性死區
console.log(Plane);
class Plane {
constructor(type) {
this.type = type
}
}
// Uncaught ReferenceError: Cannot access 'Plane' before initialization
- 類中的所有代碼均在嚴格模式下執行
class Plane {
constructor(type) {
this.type = type;
a = 1
}
}
new Plane()
// Uncaught ReferenceError: a is not defined
- 類的所有原型方法都是不可枚舉的(私有方法可枚舉)
class Plane {
constructor(type) {
this.type = type; // 私有屬性可枚舉
this.fn = () => {}; // 私有方法可枚舉
}
fly() {console.log('fly')} // 原型方法不可枚舉
}
let p = new Plane();
for (const key in p) {
console.log(key) // type fn
}
- 類的所有方法都無法被當作構造函數使用(包括靜態方法)
class Plane {
static fn () { // 靜態方法不可new
this.name = 'a'
}
constructor(type) {
this.type = type;
}
fly() {console.log('fly')} // 原型方法不可new
}
let p = new Plane();
new Plane.fn(); // Uncaught TypeError: Plane.fn is not a constructor
new p.fly(); // Uncaught TypeError: p.fly is not a constructor
- 類的構造器必須使用 new 來調用
class Plane {
constructor(type) {
this.type = type;
}
}
Plane(); // Uncaught TypeError: Class constructor Plane cannot be invoked without 'new'
類的其他書寫方式
- 可計算屬性名
let flyName = 'fly'
class Plane {
constructor(type) {
this.type = type;
}
[flyName]() {console.log('fly')} // 計算屬性名
}
let p = new Plane();
p[flyName]()
- getter和setter
// 假設飛機壽命只能爲0-10年
class Plane {
constructor(type, age) {
this.type = type;
this.age = age
}
set age(age) {
if(age < 0) {
this._age = 0;
} else if (age > 10) {
this._age = 10
} else {
this._age = age
}
}
get age() {
return this._age + '歲'
}
}
let p = new Plane('plane', 2);
console.log(p.age) // 2歲
- 靜態成員
class Plane {
static canFly = true;
constructor(type, age) {
this.type = type;
}
}
console.log(Plane.canFly); // true
- 字段初始化器
class Plane {
constructor(type) {
this.type = type;
}
name = '飛機'; // name屬性是私有屬性
// 這種寫法會將print變爲私有方法
// 由於使用了箭頭函數,箭頭函數沒有this,所以this永遠爲實例對象
print = () => {
console.log(this.type)
}
}
let p = new Plane('普通飛機');
p.print(); // 普通飛機
let print = p.print; // 即使改變執行環境,也會輸出p的type
print(); // 普通飛機
- 類表達式
let a = class { // 等同於 class a {...}
constructor() {
this.a = 1;
this.b = 2;
}
}
console.log(new a())
繼承
繼承有兩個新增關鍵字extends和super
extends用於繼承
super兩個用法
在constructor裏面要先調用super(向父類傳入所需參數)
在方法裏面使用super,super代表父類的原型
用法如下
class Animal {
constructor(type, name, age, sex) {
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
print() {
console.log(`【種類】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年齡】:${this.age}`);
console.log(`【性別】:${this.sex}`);
}
}
class Dog extends Animal {
constructor(name, age, sex) {
super('犬類', name, age, sex); // 繼承的子類在constructor裏面先調super方法,傳入父類所需參數
this.like = '喫骨頭'; // 然後添加子類的特有屬性(不能寫在super前面)
}
print() { // 覆蓋父類方法
super.print(); // 調用父類方法使用super
console.log(`【愛好】:${this.like}`);
}
}
const a = new Dog("旺財", 3, "男");
a.print()
冷知識
一般情況下,父類是不可以直接new的,創建實例對象是通過子類構造函數來創建,所以可以給父類構造函數做一個處理
class Animal {
constructor(type, name, age, sex) {
if (new.target == Animal) { // 可以通過new.target獲得創建實例的構造函數
throw('不能直接通過Animal創建實例')
}
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
print() {
console.log(`【種類】:${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年齡】:${this.age}`);
console.log(`【性別】:${this.sex}`);
}
}
class Dog extends Animal {
constructor(name, age, sex) {
super('犬類', name, age, sex); // 繼承的子類在constructor裏面先調super方法,傳入父類所需參數
this.like = '喫骨頭'; // 添加子類的特有屬性
}
print() { // 覆蓋父類方法
super.print(); // 調用父類方法使用super
console.log(`【愛好】:${this.like}`);
}
}