类定义
- class Person {} — 类的声明
- const Animal = class {}; — 类的表达式
- 类的声明不像函数一样可以声明提升
- 函数受函数作用域限制,类受块级作用域限制
类的构成
- 构造函数方法 constructor() {}
- 实例方法 class Foo {}
- 获取函数 设置函数 get baz() set baz()
- 静态类方法 static baz() {}
类构造函数
- 在内存中创建一个新对象
- 在这个新对象的内部[[Prototype]]指针被赋值为构造函数的prototype属性
- 构造函数内部的this被赋值为这个新对象(即this指向的这个对象)
- 执行构造函数内部的代码(给新对象添加属性)
- 如果构造函数返回非空对象,则返回该对象;否则返回刚刚创建的对象。 如果返回的是普通对象,instanceof失效
实例化时传入的参数,会当做constructor的参数
class Person {
constructor(name) {
console.log(arguments.length);
this.name = name || null;
}
}
类在不用new实例时,会报错
类可以立即实例化
let p = new class Foo {
constructor(x) {
console.log(x);
}
}('bar'); // bar
实例成员
所有成员是独立的
- 定义的方法,都是在原型上的,可以用变量定义方法名
- 也支持 set 和get访问器
静态方法定义在 类本身,在调用的时候直接调用,调用的时候,非常使用实例工厂
class Person {
constructor(age) {
this.age_ = age;
}
sayAge() {
console.log(this.age_);
}
static create() {
// 使用随机年龄创建并返回一个 Person 实例
return new Person(Math.floor(Math.random()*100));
}
}
console.log(Person.create()); // Person { age_: ... }
不能直接定义在原型上的属性,但是可以通过手动添加 Person.prototype.name = ‘Jake’;
可以使用迭代器和生成器方法
class Person {
// 在原型上定义生成器方法
*createNicknameIterator() {
yield 'Jack';
yield 'Jake';
yield 'J-Dog';
}
// 在类上定义生成器方法
static *createJobIterator() {
yield 'Butcher';
yield 'Baker';
yield 'Candlestick maker';
}
}
let jobIter = Person.createJobIterator();
console.log(jobIter.next().value); // Butcher
console.log(jobIter.next().value); // Baker
console.log(jobIter.next().value); // Candlestick maker
let p = new Person();
let nicknameIter = p.createNicknameIterator();
console.log(nicknameIter.next().value); // Jack
console.log(nicknameIter.next().value); // Jake
console.log(nicknameIter.next().value); // J-Dog
继承
虽然类继承使用的是新语法,但背后依旧使用的是原型链。
使用extends关键字,就可以拥有父亲身上的属性和原型对象
class Vehicle {}
// 继承类
class Bus extends Vehicle {}
let b = new Bus();
console.log(b instanceof Bus); // true
console.log(b instanceof Vehicle); // true
function Person() {}
// 继承普通构造函数
class Engineer extends Person {}
let e = new Engineer();
console.log(e instanceof Engineer); // true
console.log(e instanceof Person); // true
super关键字
子类方法可以通过super关键字引用他们的原型 — super只用用于子类中
- 在静态方法中可以通过 super 调用继承的类上定义的静态方法
- super 只能在派生类构造函数和静态方法中使用
- 不能单独引用 super 关键字,要么用它调用构造函数,要么用它引用静态方法
- 调用 super()会调用父类构造函数,并将返回的实例赋值给 this
- super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入
- 如果没有定义类构造函数,在实例化派生类时会调用 super(),而且会传入所有传给派生类的 参数
- 在类构造函数中,不能在调用 super()之前引用 this
- 如果在派生类中显式定义了构造函数,则要么必须在其中调用 super(),要么必须在其中返回 一个对象
抽象类
// 抽象基类
class Vehicle {
constructor() {
if (new.target === Vehicle) {
throw new Error('Vehicle cannot be directly instantiated');
}
// 用this判断是否是抽象类,进行了new调用,可以用ts
if (!this.foo) {
throw new Error('Inheriting class must define foo()');
}
console.log('success!');
}
}
扩展原始类型
实例的类型与原始实例的类型是一致的
class SuperArray extends Array {}
let a1 = new SuperArray(1, 2, 3, 4, 5);
let a2 = a1.filter(x => !!(x%2))
console.log(a1); // [1, 2, 3, 4, 5]
console.log(a2); // [1, 3, 5]
console.log(a1 instanceof SuperArray); // true
console.log(a2 instanceof SuperArray); // true
可以覆盖 Symbol.species 访问器
类混入
class Vehicle {}
let FooMixin = (Superclass) => class extends Superclass {
foo() {
console.log('foo');
}
};
let BarMixin = (Superclass) => class extends Superclass {
bar() {
console.log('bar');
}
};
let BazMixin = (Superclass) => class extends Superclass {
baz() {
console.log('baz');
}
};
function mix(BaseClass, ...Mixins) {
return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass);
}
class Bus extends mix(Vehicle, FooMixin, BarMixin, BazMixin) {}
let b = new Bus();
b.foo(); // foo
b.bar(); // bar
b.baz(); // baz
- 很多 JavaScript 框架(特别是 React)已经抛弃混入模式,转向了组合模式