定义类
ES6类并不是一个全新的东西:它们主要提供更方便的语法来创建老式的构造函数,javascript的类并不像其他面向对象语言C++,java中的类,这里的类只是语法糖,实际上还是基于原型链的方式。
类申明
定义类的一种方式是使用类的申明,为了申明一个类,你可以使用class关键字空格后接一个名字。
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
函数声明和类申明一个最大的不同是,你必须先申明类才能使用,否则会抛出一个ReferenceError错误,而函数则没有这个限制。也就是说函数申明会提升而类不会,下面代码就会报错:
const p = new Rectangle(100, 30);
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
类表达式
类表达式是另外一种定义类的方法,类表达式可以是命名也可以是匿名的。如果是命名类表达式,这个名字只能在类体内部才能访问到。JavaScript的类也是基于原型继承的。
const Rt1 = class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
sayName() {
console.log(Rectangle.name);
}
}
const r1 = new Rt1(100, 20);
r1.sayName();
const Rt2 = class {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
类的主体和方法定义
大括号里面就是类的主体部分,在这里可以定义类的成员,如:方法或者构造函数。
严格模式
类声明和类表达式的主体在严格模式下执行,即构造函数,静态和原型方法,getter和setter函数以严格模式执行。
构造函数
构造函数是一个特殊的函数,一个类中有且仅有一个这样的方法,当创建一个类的对象的时候被调用。你也可以不写构造函数,但是系统会默认加上。
基类默认加上如下构造:
constructor() {}
派生类默认加上如下构造:
constructor(...args) {
super(...args);
}
原型方法
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}
const p = new Rectangle(100, 30)
console.log(p.area);
静态方法
使用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.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2)); // 7.0710678118654755
getter and setter方法
这里有不少坑,用之前要了解清楚,能不用就尽量不用。
class Widget {
get prop() {
return 'getter'
}
set prop(value) {
console.log('setter, ' + value)
}
}
const p = new Widget();
p.prop = 123;
console.log(p.prop)
构造函数,静态方法,原型方法
下面是一个比较普遍的用法
class Foo {
constructor(prop) {
this.prop = prop;
}
static staticMethod() {
return 'classy';
}
prototypeMethod() {
return 'prototypical';
}
}
const foo = new Foo(123);
通过对象图来看一下它们之间的关系:
继承
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color;
}
}
const p1 = new Point(3, 4);
const p2 = new ColorPoint(5, 6, 'red');
console.log(p1.toString());
console.log(p2.toString());
es6中的类并不是一个新的东西,它只是提供了更方便的语法去创建老式的构造函数,使用typeof Point可以看到它其实还是个函数。
在子类的构造函数中必须调用super(),同时super的调用必须在使用this之前,将上面的构造函数改为:
constructor(x, y, color) {
this.color = color;
super(x, y);
}
运行代码会报错:
ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor
再通过下面代码看一下继承的原型链图表
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `Person named ${this.name}`;
}
static logNames(persons) {
for (const person of persons) {
console.log(person.name);
}
}
}
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
toString() {
return `${super.toString()} (${this.title})`;
}
}
const jane = new Employee('Jane', 'CTO');
console.log(jane.toString()); // Person named Jane (CTO)