ECMAScript 6 引入了 JavaScript 类,给原有的JavaScript原型继承提供了语法糖. 类的语法并没有向JavaScript引进一个新的面向对象的继承模式. classes为创建对象,处理继承提供了简单,清晰的语法.
定义类
类其实是”特殊的函数”.就像你可以定义 表达式函数 function expressions 和 声明式函数 function declaration 一样,类也有两种形式 表达式类 和 声明式类
声明式类
定义类的方式之一就是声明式类. 要声明一个类,可以使用 class
关键字,加上类的名字(在这里叫”Ploygon”).
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
预声明
函数声明和类声明的一个重要区别在于,函数会预声明,而类不会.你必须先声明类,然后再使用它.像下面这样的代码会报错: ReferenceError.
var p = new Polygon(); // ReferenceError
class Polygon {}
表达式类
表达式类是定义类的另一种方式. 表达式类可以有名字也没有没有名字.给一个已经有名字的表达式类一个变量名,两个名字必须相等…(正常人都不会这样玩吧…)
// unnamed
var Polygon = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// named
var Polygon = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
类体和方法的定义
{}里的部分就被成为类体.这是你定义类内容的地方,比如类的方法和构造器.
严格模式
类体(无论是声明式类还是表达式类)里的代码,都是以严格模式执行的.
Constructor
constructor是一个特殊的方法,用于从所在的 class
创建和实例化对象.在一个类里,只能有一个名叫 “constructor” 的特殊方法.如果 constructor
方法在一个类里出现一次以上,会报错: SyntaxError
constructor可以使用 super
关键字来调用父类的constructor
原型方法
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
get area() {
return this.calcArea();
}
calcArea() {
return this.height * this.width;
}
}
静态方法
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));
通过 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.');
}
}
通过 super
调用超类
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.');
}
}
混合(Mix-ins)
抽象子类或混合,是类的模板.ECMAScript类只能拥有一个超类,所以,继承多个工具类是不可行的. 所有的工具函数都必须又超类提供.
想要在 ECMAScript 里实现混合,可以创造一个函数,它接受一个超类作为输入参数,然后输出一个继承该超类的子类:
var CalculatorMixin = Base => class extends Base {
calc() { }
};
var RandomizerMixin = Base => class extends Base {
randomize() { }
};
一个使用了这些混合的类看起来应该是这样的:
class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { }
这个可能比较难理解,因为我把以上代码转换成es5以后,发现代码非常多,非常长…所以还是先从概念上去理解…
首先,我把这个例子写的更深入一点:
"use strict";
var CalculatorMixin = Base => class extends Base {
calc() {
return 'calcResult';
}
};
var RandomizerMixin = Base => class extends Base {
randomize() {
return 'randomizeResult'
}
};
var Bunny = Base => class extends Base {
bunny() {
return 'bunny'
}
};
class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Bunny(Foo))) { }
var bar = new Bar();
console.log(bar.calc()); //'calcResult'
console.log(bar.randomize()); //'dandomizeResult'
console.log(bar.bunny()); //'bunny'
通过这个例子,可以很好的明白,ECMAScript的”混合”是在做什么: 它可以让一个类去包含多个”混合”里的工具函数,然后实例化这个类的时候,实例也会继承来自多个”混合”的方法.