es6 引入的 class 類實質上是 JavaScript 基於原型繼承的語法糖。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}`;
}
}
複製代碼
類由兩部分組成:類聲明,類表達式java
class Animal {
constructor(name) {
this.name = name;
}
}
複製代碼
類其實是個特殊的函數
,普通函數聲明和類函數聲明有一個重要的區別就是函數 聲明會提高,而類聲明不會。若是先訪問,後聲明就會拋出相似於下面的錯誤。es6
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
。ui
constructor
方法是類的默認方法,經過new
建立對象實例時,自動會調用該方法, 一個類必須擁有constructor
方法,若是沒有寫,JavaScript 引擎會默認加上空的constructor
方法。class Animal {}
// 等同於
class Animal {
constructor() {}
}
複製代碼
constructor
方法默認返回實例對象(既this
),徹底能夠指定返回另一個對象this
class Animal {
constructor() {
return Object.create(null);
}
}
new Animal() instanceof Animal; // false;
複製代碼
上面代碼中,constructor
函數返回一個全新的對象,結果致使實例對象不是Animal
類的實例。spa
嚴格模式 類和模塊的內部,默認就是嚴格模式,好比,構造函數,靜態方法,原型方法,getter 和 setter 都在嚴格模式下執行。prototype
類的實例 類的實例,經過 new 建立, 建立時會自動調用構造函數code
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
複製代碼
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
複製代碼
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
做爲函數調用時,表明父類的構造函數。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;
複製代碼