1.定義類 2.類聲明 3.變數提升 4.類表達式 匿名的 命名的 5.原型方法 6.靜態方法 7.類繼承 8.Species 9.super 關鍵字可以用來調用其父類的構造器或者類方法 上面代碼首先用class定義了一個“類”,可以看到裡面有一個constructor方法,這就是構造方法,而thi ...
ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。
部分來自JavaScript ES6 class指南、mozilla https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
1.定義類
讓我們回想一下在ES5中定義一個類的方式。通過不是很常用的Object.defineProperty方法,我可以定義一些只讀的屬性。
function Vehicle1(make, year) {
Object.defineProperty(this, 'make', {
get: function() { return make; }
});
Object.defineProperty(this, 'year', {
get: function() { return year; }
});
}
Vehicle1.prototype.toString = function() {
return this.make + ' ' + this.year;
}
var vehicle1 = new Vehicle1('Toyota Corolla', 2009);
console.log(vehicle1.make); // Toyota Corolla
vehicle1.make = 'Ford Mustang';
console.log(vehicle1.toString()) // Toyota Corolla 2009
很簡單,我們定義了一個有兩個只讀屬性和一個自定義toString方法的Vehicle類。讓我們在ES6中來做一樣的事情:
class Vehicle {
constructor(make, year) {
this._make = make;
this._year = year;
}
get make() {
return this._make;
}
get year() {
return this._year;
}
toString() {
return `${this.make} ${this.year}`;
}
}
var vehicle = new Vehicle('Toyota Corolla', 2009);
console.log(vehicle.make); // Toyota Corolla
vehicle.make = 'Ford Mustang';
console.log(vehicle.toString()) // Toyota Corolla 2009
上面兩個例子中定義的類有一個不同的地方。我們為了享受新的get語法帶來的好處,所以只是將make和year定義成了普通的屬性。這使它們可以被外部所改變。如果你確實需要一個嚴格的私有屬性,還是請繼續使用defineProperty。
2.類聲明
類聲明是定義類的一種方式,就像下麵這樣,使用 class 關鍵字後跟一個類名(這裡是 Polygon),就可以定義一個類。
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
3.變數提升
類聲明和函數聲明不同的一點是,函數聲明存在變數提升現象,而類聲明不會。
也就是說,你必須先聲明類,然後才能使用它,否則代碼會拋出 ReferenceError 異常,像下麵這樣:
var p = new Polygon(); // ReferenceError
class Polygon {}
4.類表達式
類表達式是定義類的另外一種方式。在類表達式中,類名是可有可無的。如果定義了類名,則該類名只有在類體內部才能訪問到。
匿名的
var Polygon1 = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
命名的
var Polygon2 = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
註意: 類表達式和類聲明一樣也不會有提升的現象。
5.原型方法
class Polygon3 {
constructor(height, width) {
this.height = height;
this.width = width;
}
get area() {
return this.calcArea()
}
calcArea() {
return this.height * this.width;
}
}
const square = new Polygon3(10, 10);
console.log(square.area);// 100
6.靜態方法
static 關鍵字用來定義類的靜態方法。靜態方法是指那些不需要對類進行實例化,使用類名就可以直接訪問的方法,需要註意的是靜態方法不能被實例化的對象調用。靜態方法經常用來作為工具函數。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx*dx + dy*dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
7.類繼承
使用 extends 創建子類
extends 關鍵字可以用在類聲明或者類表達式中來創建一個繼承了某個類的子類。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak();// 'Mitzie barks.'
//同樣也可以用於原有的原型繼承的“類”:
function Animal2 (name) {
this.name = name;
}
Animal2.prototype.speak = function () {
console.log(this.name + ' makes a noise.');
}
class Dog2 extends Animal2 {
speak() {
super.speak();
console.log(this.name + ' barks.');
}
}
var d2 = new Dog2('Mitzie');
d2.speak();
//需要註意的是類不能繼承一般(非構造的)對象。如果你想要創建的類繼承某個一般對象的話,你要使用 Object.setPrototypeOf():
var Animal3 = {
speak() {
console.log(this.name + ' makes a noise.');
}
};
class Dog3 {
constructor(name) {
this.name = name;
}
speak() {
super.speak();
console.log(this.name + ' barks.');
}
}
Object.setPrototypeOf(Dog3.prototype, Animal3);
var d3 = new Dog3('Mitzie');
d3.speak();
8.Species
你可能想要數組類 MyArray 返回的是 Array 對象。這個 species 模式能讓你重寫預設的構造器。
例如,當使用像 map() 這樣的方法來返回預設的構造器時,你想要這個方法返回父級的 Array 對象,而不是 MyArray 對象。Symbol.species 能實現:
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
9.super 關鍵字可以用來調用其父類的構造器或者類方法
class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(this.name + ' roars.');
}
}
new Lion('nick').speak();
//再看個重要的例子
class Animal4 {
constructor(){
this.type = 'animal';
}
says(say){
console.log(this.type + ' says ' + say);
}
}
let animal4 = new Animal4();
animal4.says('hello') //animal says hello
class Cat4 extends Animal4 {
constructor(){
super();
this.type = 'cat';
// 1.子類定義constructor;
// 2.調用super();
// 3.this就會指向子類實例cat4,否則constructor內this報錯或子類方法內this指向父類實例
}
}
let cat4 = new Cat4();
cat4.says('hello'); //cat says hello
上面代碼首先用class定義了一個“類”,可以看到裡面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。
簡單地說,constructor內定義的方法和屬性是實例對象自己的,而constructor外定義的方法和屬性則是所有實例對象可以共用的。
Class之間可以通過extends關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承,要清晰和方便很多。
上面定義了一個Cat類,該類通過extends關鍵字,繼承了Animal類的所有屬性和方法。
super關鍵字,它指代父類的實例(即父類的this對象)。子類必須在constructor方法中調用super方法,否則新建實例時會報錯。
這是因為子類沒有自己的this對象,而是繼承父類的this對象,然後對其進行加工。如果不調用super方法,子類就得不到this對象。
ES6的繼承機制,實質是先創造父類的實例對象this(所以必須先調用super方法),然後再用子類的構造函數修改this。
es6暫終