類
es6 引入的 class 類實質上是 JavaScript 基於原型繼承的語法糖。
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHi = () => {
return `Hello ${this.name}`;
};
// 等同於
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hello ${this.name}`;
}
}
複製程式碼
類由兩部分組成:類宣告,類表示式
- 類宣告
class Animal {
constructor(name) {
this.name = name;
}
}
複製程式碼
類實際上是個特殊的函式
,普通函式宣告和類函式宣告有一個重要的區別就是函式
宣告會提升,而類宣告不會。如果先訪問,後宣告就會丟擲類似於下面的錯誤。
let animal = new Animal();
// Uncaught ReferenceError: Cannot access 'Animal' before initialization
class Animal {}
複製程式碼
- 類表示式
類表示式可以是被命名的或匿名的,(ps: 類表示式也同樣受到類宣告中提到的提升問題的困擾。)
// 匿名類
let Animal = class {
constructor(name) {
this.name = name;
}
};
// 命名類
let Animal = class Cat {
constructor(name) {
this.name = name;
}
getClassName() {
return Cat.name;
}
};
複製程式碼
此時類名字Cat
只能在 class 內部使用,指代當前類,在類的外部只能用Animal
。
- 建構函式
constructor
方法是類的預設方法,通過new
建立物件例項時,自動會呼叫該方法, 一個類必須擁有constructor
方法,如果沒有寫,JavaScript 引擎會預設加上空的constructor
方法。
class Animal {}
// 等同於
class Animal {
constructor() {}
}
複製程式碼
constructor
方法預設返回例項物件(既this
),完全可以指定返回另外一個物件
class Animal {
constructor() {
return Object.create(null);
}
}
new Animal() instanceof Animal; // false;
複製程式碼
上面程式碼中,constructor
函式返回一個全新的物件,結果導致例項物件不是Animal
類的例項。
-
嚴格模式 類和模組的內部,預設就是嚴格模式,比如,建構函式,靜態方法,原型方法,getter 和 setter 都在嚴格模式下執行。
-
類的例項 類的例項,通過 new 建立, 建立時會自動呼叫建構函式
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return "My name is " + this.name;
}
}
let animal = new Animal("rudy");
animal.sayHi(); // My name is rudy
複製程式碼
- 存取器
與 ES5 一樣,在類的內部可以使用
get
和set
關鍵字,對某個屬性設定存取 函式和取值函式,攔截該屬性的存取行為。
class Animal {
constructor(name) {
this.name = name;
}
get name() {
return "rudy";
}
set name(value) {
console.log("setter, " + this.value);
}
}
let animal = new Animal("rudy");
animal.name = "Tom"; // setter, Tom
console.log(a.name); // rudy
複製程式碼
- 靜態方法
使用
static
修飾符修飾的方法稱為靜態,它們不需要例項化,直接通過類來呼叫。
class Animal {
static sayHi(name) {
console.log("i am " + name);
}
}
let animal = new Animal();
Animal.sayHi("rudy"); // i am rudy
animal.sayHi("rudy"); // Uncaught TypeError: animal.sayHi is not a function
複製程式碼
- 例項屬性,靜態屬性
ES6 中的例項屬性只能通過建構函式中的
this.xxx
來定義,但最近 ES7 中可以直接在類裡面定義:
class Animal {
name = "rudy";
static value = 11;
sayHi() {
console.log(`hello, ${this.name}`);
}
}
let animal = new Animal();
animal.sayHi(); // hello, rudy
Animal.value; // 11
animal.value; // undefiend
複製程式碼
- 類的繼承
使用
extends
關鍵字實現繼承,子類中使用super
關鍵字來呼叫父類的建構函式和方法。
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return "this is " + this.name;
}
}
class Cat extends Animal {
constructor(name, value) {
super(name); // 呼叫父類的 constructor(name)
this.value = value;
}
sayHi() {
return `omg, ${super.sayHi()} it is ${this.value}`;
}
}
let cat = new Cat("Tom", 11);
cat.sayHi(); // omg, this is Tom it is 11;
複製程式碼
- super 關鍵字
super
這個關鍵字,既可以當著函式使用,也可以當著物件使用。兩種情況下,用法完全不同。
第一種情況,super
作為函式呼叫時,代表父類的建構函式。ES6 要求,字類的建構函式必須執行一次super
函式。
class Animal {}
class Cat extends Animal {
constructor() {
super();
}
}
複製程式碼
上面程式碼中,子類Cat
的建構函式中的super()
,代表呼叫父類的建構函式,這是必須的,否在 JavaScript 引擎會報錯。
注意,super
雖然代表了父類Animal
的建構函式,但是返回的是字類Cat
的例項,既super
內部的this
指的是Cat
的例項,因此super()
在這裡相當於
Animal.prototype.constructor.call(this)
。
class Animal {
constructor() {
console.log(new.target.name); // new.target指向當前正在執行的函式
}
}
class Cat extends Animal {
constructor() {
super();
}
}
new Animal(); // Animal;
new Cat(); // Cat;
複製程式碼
可以看出,在super()
執行時,它指向的是子類Cat
的建構函式,而不是父類Animal
的建構函式,也就是說super
內部的this
指向是Cat
。
作為函式時,super()
只能用在子類的建構函式之中,用在其他地方就會報錯。
class Animal {}
class Cat extends Animal {
hi() {
super(); // Uncaught SyntaxError: 'super' keyword unexpected here
}
}
複製程式碼
第二種情況,super
作為物件時:
- 在普通方法中,指向父類的原型物件,
- 在靜態方法中,指向父類。
class Animal {
getName() {
return "rudy";
}
}
class Cat extends Animal {
constructor() {
super();
console.log(super.getName());
}
}
let cat = new Cat(); // rudy;
複製程式碼
上面程式碼中,子類Cat
中的super.getName()
,就是將super
當作一個物件使用,這時,super
在普通方法中,指向的是Animal.prototype
,super.getName()
相當於Animal.prototype.getName()
。
這裡需要注意,由於super
指向的是父類原型物件,所以定義在父類例項上的方法和屬性,是無法通過super
獲取到的。
class Animal {
constructor() {
this.name = "rudy";
}
}
class Cat extends Animal {
constructor() {
super();
}
getName() {
return super.name;
}
}
let cat = new Cat();
cat.getName(); // undefined;
複製程式碼
上面程式碼中,name
是父類例項的屬性,而不是父類原型物件的屬性,所以super.name
引用不到它。
用在靜態方法中,super
將指向父類,而不是父類的原型物件。
class Animal {
static getName(name) {
console.log("static", name);
}
getName(name) {
console.log("instance", name);
}
}
class Cat extends Animal {
constructor() {
super();
}
static getName(name) {
super.getName(name);
}
getName(name) {
super.getName(name);
}
}
Cat.getName("rudy"); // static rudy;
let cat = new Cat();
cat.getName("tom"); // instance tom;
複製程式碼
在上面程式碼中,super
在靜態方法中指向父類,在普通方法中指向父類的原型物件。
另外,在字類的靜態方法中通過super
呼叫父類的方法時,方法內部的this
指向當前的子類,而不是子類例項。
class Animal {
constructor() {
this.name = "rudy";
}
static print() {
console.log(this.name);
}
}
class Cat extends Animal {
constructor() {
super();
this.name = 2;
}
static print() {
super.print();
}
}
Cat.name = "Tom";
Cat.print(); // Tom;
複製程式碼