在js中,從es6開始引進class,根本上是基於js中已經存在的原型繼承的語法糖,class語法並沒有引進一種新的面向對象的繼承機制。
一、定義class
class事實上是一種特殊的funcion,就像可以定義funcion表達式和funcion聲明一樣,class語法也有2種形式:class表達式和class聲明。
1、class聲明
定義一個class,可以使用class關鍵字加上類名。需要注意的是,funcion聲明和class聲明有一個重要的卻別:funcion聲明是hosting(狀態提升)的,而class不是,class需要先聲明再使用。
1 class Rectangle { 2 constructor(height, width) { 3 this.height = height; 4 this.width = width; 5 } 6 }
2、class表達式
class表達式類名可選,已經命名的class可以通過實例的'.name'屬性獲取,依據class自身的命名。
1 // unnamed 2 let Rectangle = class { 3 constructor(height, width) { 4 this.height = height; 5 this.width = width; 6 } 7 }; 8 console.log(Rectangle.name); 9 // output: "Rectangle" 10 11 // named 12 let Rectangle = class Rectangle2 { 13 constructor(height, width) { 14 this.height = height; 15 this.width = width; 16 } 17 }; 18 console.log(Rectangle.name); 19 // output: "Rectangle2"
二、class中body和method的定義
class的body部分包含在花括號{}中,這里是定義class成員的地方,比如constructor和method。
1、constructor
constructor方法是一個特殊的方法,用來創建並初始化一個對象。在一個class中只能有一個命名為constructor的特殊方法,如果包含多個將會報錯。
constructor中可以通過super關鍵字,調用父類的constructor方法。
2、prototype methods(原型方法)
1 class Rectangle { 2 constructor(height, width) { 3 this.height = height; 4 this.width = width; 5 } 6 // Getter 7 get area() { 8 return this.calcArea(); 9 } 10 // Method 11 calcArea() { 12 return this.height * this.width; 13 } 14 } 15 16 const square = new Rectangle(10, 10); 17 18 console.log(square.area); // 100
3、static methods(靜態方法)
通過static關鍵字為一個class創建靜態方法,static methods的調用無需對class實例化,也不能被實例對象所調用。
1 class Point { 2 constructor(x, y) { 3 this.x = x; 4 this.y = y; 5 } 6 7 static distance(a, b) { 8 const dx = a.x - b.x; 9 const dy = a.y - b.y; 10 11 return Math.hypot(dx, dy); 12 } 13 } 14 15 const p1 = new Point(5, 5); 16 const p2 = new Point(10, 10); 17 18 console.log(Point.distance(p1, p2)); // 7.0710678118654755
4、static和prototype method的封裝
當static或prototype method被調用的時候,如果沒有對this賦值,那么this將是undefine狀態。這和是否采用static模式無關,因為class類體中的代碼已經默認執行static模式。
1 class Animal { 2 speak() { 3 return this; 4 } 5 static eat() { 6 return this; 7 } 8 } 9 10 let obj = new Animal(); 11 obj.speak(); // Animal {} 12 let speak = obj.speak; 13 speak(); // undefined 14 15 Animal.eat() // class Animal 16 let eat = Animal.eat; 17 eat(); // undefined
如果以上代碼是基於傳統的function的語法,則this值是global object。
三、extends創建子類
extends關鍵字用於在class聲明或表達式中,創建另一個class的子類。
1 class Animal { 2 constructor(name) { 3 this.name = name; 4 } 5 6 speak() { 7 console.log(this.name + ' makes a noise.'); 8 } 9 } 10 11 class Dog extends Animal { 12 constructor(name) { 13 super(name); // call the super class constructor and pass in the name parameter 14 } 15 16 speak() { 17 console.log(this.name + ' barks.'); 18 } 19 } 20 21 let d = new Dog('Mitzie'); 22 d.speak(); // Mitzie barks.
如果在子類中有constructor方法,在使用"this"之前需要調用super()。也可以繼承自function-based的class。
1 function Animal (name) { 2 this.name = name; 3 } 4 5 Animal.prototype.speak = function () { 6 console.log(this.name + ' makes a noise.'); 7 } 8 9 class Dog extends Animal { 10 speak() { 11 console.log(this.name + ' barks.'); 12 } 13 } 14 15 let d = new Dog('Mitzie'); 16 d.speak(); // Mitzie barks.
需要注意的是:class不能繼承自常規對象(non-constructible),如果想繼承自常規對象,可以使用Object.setPrototypeOf()替代。
1 const Animal = { 2 speak() { 3 console.log(this.name + ' makes a noise.'); 4 } 5 }; 6 7 class Dog { 8 constructor(name) { 9 this.name = name; 10 } 11 } 12 13 // If you do not do this you will get a TypeError when you invoke speak 14 Object.setPrototypeOf(Dog.prototype, Animal); 15 16 let d = new Dog('Mitzie'); 17 d.speak(); // Mitzie makes a noise.
四、super關鍵字調用父類
關鍵字super用於調用父類相應的方法,這是相較於原型繼承的一個好處。
1 class Cat { 2 constructor(name) { 3 this.name = name; 4 } 5 6 speak() { 7 console.log(`${this.name} makes a noise.`); 8 } 9 } 10 11 class Lion extends Cat { 12 speak() { 13 super.speak(); 14 console.log(`${this.name} roars.`); 15 } 16 } 17 18 let l = new Lion('Fuzzy'); 19 l.speak(); 20 // Fuzzy makes a noise. 21 // Fuzzy roars.