类定义
- 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); // Butcherconsole.log(jobIter.next().value); // Bakerconsole.log(jobIter.next().value); // Candlestick makerlet p = new Person();let nicknameIter = p.createNicknameIterator();console.log(nicknameIter.next().value); // Jackconsole.log(nicknameIter.next().value); // Jakeconsole.log(nicknameIter.next().value); // J-Dog
继承
虽然类继承使用的是新语法,但背后依旧使用的是原型链。
使用extends关键字,就可以拥有父亲身上的属性和原型对象
class Vehicle {}// 继承类class Bus extends Vehicle {}let b = new Bus();console.log(b instanceof Bus); // trueconsole.log(b instanceof Vehicle); // truefunction Person() {}// 继承普通构造函数class Engineer extends Person {}let e = new Engineer();console.log(e instanceof Engineer); // trueconsole.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调用,可以用tsif (!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); // trueconsole.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(); // foob.bar(); // barb.baz(); // baz
- 很多 JavaScript 框架(特别是 React)已经抛弃混入模式,转向了组合模式
