9.1 class
基本的类语法看起来像这样:
class MyClass {prop = value; // 属性constructor(...) { // 构造器// ...}method(...) {} // methodget something(...) {} // getter 方法set something(...) {} // setter 方法[Symbol.iterator]() {} // 有计算名称(computed name)的方法(此处为 symbol)// ...// =定义的函数和属性都在对象实例上// 等价于写在constructor里加this.sayHi = ...sayHi = function() {alert('Hi')}}
技术上来说,MyClass 是一个函数(我们提供作为 constructor 的那个),而 methods、getters 和 settors 都被写入了 MyClass.prototype。
9.2 类继承
使用 extends 继承
class Animal {constructor(name) {this.speed = 0;this.name = name;}run(speed) {this.speed = speed;alert(`${this.name} runs with speed ${this.speed}`);}stop() {this.speed = 0;alert(`${this.name} stands still`);}}class Rabbit extends Animal {hide() {alert(`${this.name} hides`);}}let rabbit = new Rabbit("White Rabbit");rabbit.run(5);rabbit.hide();rabbit.__proto__ === Rabbit.prototype;Rabbit.prototype.__proto__ === Animal.prototype;Animal.prototype.__proto__ === Object.prototype;
重写方法,使用 super 调用父类的方法
class Rabbit extends Animal {hide() {alert(`${this.name} hides!`);}stop() {super.stop(); // 调用父类的 stopthis.hide(); // 然后 hide}}
重写 constructor
继承类的 constructor 必须调用 super(…),并且 (!) 一定要在使用 this 之前调用。
class Rabbit extends Animal {constructor(name, earLength) {super(name);this.earLength = earLength;}// ...}
箭头函数没有自己的 super
9.3 静态属性和静态方法
静态方法被用于实现属于整个类的功能。它与具体的类实例无关。
语法如下所示:
class MyClass {static property = ...;static method() {...}}
从技术上讲,静态声明与直接给类本身赋值相同:
MyClass.property = ...MyClass.method = ...
静态属性和方法是可被继承的。
对于 class B extends A,
B.__proto__ === A;B.prototype.__proto__ === A.prototype;
因此,如果一个字段在 B 中没有找到,会继续在 A 中查找。
继承 Object 的问题
class Rabbit extends Object {constructor(name) {super(); // 需要在继承时调用父类的 constructorthis.name = name;}}let rabbit = new Rabbit("Rab");alert(rabbit.hasOwnProperty("name")); // true
“extends” 语法会设置两个原型:
- 在构造函数的 “prototype” 之间设置原型(为了获取实例方法)。
- 在构造函数之间会设置原型(为了获取静态方法)。
9.4 私有的和受保护的属性和方法
面向对象编程最重要的原则之一 —— 将内部接口与外部接口分隔开来。
在面向对象的编程中,属性和方法分为两组:
- 内部接口 —— 可以通过该类的其他方法访问,但不能从外部访问的方法和属性。
- 外部接口 —— 也可以从类的外部访问的方法和属性。
受保护的 waterAmount
class CoffeeMachine {// 受保护的属性通常以下划线 _ 作为前缀_waterAmount = 0;set waterAmount(value) {if (value < 0) throw new Error("Negative water");this._waterAmount = value;}get waterAmount() {return this._waterAmount;}constructor(power) {this.power = power;}}let coffeeMachine = new CoffeeMachine(100);coffeeMachine.waterAmount = -10;
只读的 power
要让一个属性变得只读,只设置 getter,不设置 setter
class CoffeeMachine {// ...constructor(power) {this._power = power;}get power() {return this._power;}}let coffeeMachine = new CoffeeMachine(100);coffeeMachine.power = 200; // Error
私有字段
私有字段用#开头,是语言级别的实现
class CoffeeMachine {#waterLimit = 200;#checkWater(value) {if (value < 0) throw new Error("Negative water");if (value > this.#waterLimit) throw new Error("Too much water");}}let coffeeMachine = new CoffeeMachine();// 不能从类的外部访问类的私有属性和方法coffeeMachine.#checkWater(); // ErrorcoffeeMachine.#waterLimit = 1000; // Error
私有属性限制太严重,更多还是用受保护的字段
就面向对象编程(OOP)而言,内部接口与外部接口的划分被称为 封装
9.5 instanceof 操作符
obj instanceof Class;
如果 obj 隶属于 Class 类(或 Class 类的衍生类)或者 构造函数,则返回 true。
instanceof 考虑原型链,如果 Class 在 obj 的原型链中,则返回 true
这里还要提到一个方法 objA.isPrototypeOf(objB),如果 objA 处在 objB 的原型链中,则返回 true。所以,可以将 obj instanceof Class 检查改为 Class.prototype.isPrototypeOf(obj)。
instanceof 只关心 prototype,不关心 constructor
