Class
基础
创建 Class
// class 声明
class Person {}
// class 表达式
const Animal = class {};
构造函数
- constructor 可选, 默认为一个空函数;
- 定义实例属性和实例方法;
const Person = class {
constructor() {
console.log("person ctor");
}
};
实例化
- 强制使用 new 关键字;
- 创建一个 object;
- object 的 prototype 指向 constructor 的 prototype;
- constructor 的 this 指向 object;
- 执行 constructor 内语句;
- 若 constructor 返回一个 object, 则返回该 object, 否则返回新创建的对象;
const Person = class {
constructor() {
console.log("0");
}
};
let p1 = new Person(); // 0
类方法
实例/原型/静态方法
- 实例成员: 定义在 constructor 中, 使用 this 定义, 不同实例之间相互隔离;
- 原型方法: 定义在 class.prototype 中, 不同实例之间共享;
- 静态方法: 定义在 class 对应的 object 上, 不需要实例化即可访问;
class Person {
constructor() {
this.locate = () => console.log("instance", this);
}
locate() {
console.log("prototype", this);
}
static locate() {
console.log("class", this);
}
}
let p = new Person();
p.locate(); // instance, Person {}
Person.prototype.locate(); // prototype, {constructor: ... }
Person.locate(); // class, class Person {}
访问器
- 只定义 get, 表明只读;
- 只定义 set, 表示只写;
class Person {
set name(newName) {
this._name = newName;
}
get name() {
return this._name;
}
}
let p = new Person();
p.name = "Jake";
console.log(p.name); // Jake
两种函数形式的 this 指向
- 普通函数的 this 指向函数被调用时所在词法环境;
- 箭头函数的 this 指向箭头函数被创建时所在词法环境;
- 但是箭头函数形式等效于在 constructor 中定义函数, 每个实例之间不会共享;
class Test {
color = "red";
// 普通函数
fun() {
console.log(this.color);
}
// 箭头函数
arrow = () => {
console.log(this.color);
};
}
const instance = new Test();
const a = {
color: "green",
};
a.fun = instance.fun;
a.arrow = instance.arrow;
instance.fun(); // red
instance.arrow(); // red
a.fun(); // green
a.arrow(); // red
强制绑定 this
- 在构造器里显式调用 bind 函数绑定 this;
- 保证 this 指向 class 实例, 且不同实例之间共享方法;
class Person {
constructor(name) {
this.name = name;
this.talk = this.talk.bind(this);
}
talk() {
console.log(`${this.name} says hello`);
}
}
类继承
继承基础
- 使用 extends 关键字 + super 方法;
- 子类继承父类所有的属性和方法;
- constructor 必须使用 super;
- 只能用于子类 constructor 和 static method 中;
- 用于调用父类的 constructor 并赋值给子类的 this;
- 当不定义 constructor 时, 自动调用 super;
class Vehicle {
constructor() {
this.hasEngine = true;
}
}
class Bus extends Vehicle {
constructor() {
// 调用 super() 之前, 子类无法使用 this
// 自定义 constructor 必须使用 super(), 或返回一个自定义 object
super();
console.log(this instanceof Vehicle); // true
console.log(this); // Bus { hasEngine: true }
}
}
抽象基类
抽象基类
- 使用 new.target 属性, 禁止实例化;
class Vehicle {
constructor() {
console.log(new.target);
if (new.target === Vehicle) {
throw new Error("Vehicle cannot be directly instantiated");
}
}
}
定义必须实现的方法
- constructor 中判断 this 是否存在对应方法;
class Vehicle {
constructor() {
if (new.target === Vehicle) {
throw new Error("Vehicle cannot be directly instantiated");
}
if (!this.foo) {
throw new Error("Inheriting class must define foo()");
}
console.log("success!");
}
}
多类继承
- js 没有显式支持多类继承, 只能手动模拟;
class Vehicle {}
let FooMixin = (Superclass) =>
class extends Superclass {
foo() {
console.log("foo");
}
};
let BarMixin = (Superclass) =>
class extends Superclass {
bar() {
console.log("bar");
}
};
class Bus extends FooMixin(BarMixin(Vehicle)) {}
let b = new Bus();
b.foo(); // foo
b.bar(); // bar
类进阶
class 的本质
- js 中的类就是一个特殊的函数;
const Person = class {};
console.log(Person); // class Person {}
console.log(typeof Person); // function
prototype 属性
- class 具有 prototype 属性;
- 对应原型对象具有 constructor 属性, 指向 class 本身;
类的构造函数实现
- 继承通过组合寄生继承实现;
// 实例成员
function MyClass(name) {
this.name = name;
}
// 原型方法
MyClass.prototype.sayHello = function () {
console.log("Hello, " + this.name + "!");
};
// 静态方法
MyClass.staticMethod = function () {
console.log("This is a static method.");
};
迭代器和生成器
定义迭代器
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
定义默认 iterator
class Person {
constructor() {
this.nicknames = ["Jack", "Jake", "J-Dog"];
}
*[Symbol.iterator]() {
yield* this.nicknames.entries();
}
}
let p = new Person();
for (let [idx, nickname] of p) {
console.log(nickname);
}
// Jack
// Jake
// J-Dog