Class 基本语法
- “class” 语法
JavaScript
class MyClass {
// class 方法
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
1. 类的方法之间没有逗号
- 什么是 class?
- 在 JavaScript 中,类是一种函数。
- 不仅仅是语法糖
- 类方法不可枚举。
- 类总是使用
use strict
- 首先,通过
class
创建的函数具有特殊的内部属性标记[[IsClassConstructor]]: true
。
- 类表达式
- 就像函数一样,类可以在另外一个表达式中被定义,被传递,被返回,被赋值等。
- 类似于命名函数表达式(Named Function Expressions),类表达式可能也应该有一个名字。
- Getters/setters
- 就像对象字面量,类可能包括 getters/setters,计算属性(computed properties)等。
- 计算属性名称 […]
- Class 字段
- 使用类字段制作绑定方法
- 正如 函数绑定 一章中所讲的,JavaScript 中的函数具有动态的
this
。
类继承
类继承是一个类扩展另一个类的一种方式。
- “extends” 关键字
JavaScript
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.`);
}
}
let animal = new Animal("My animal");
1. 在 `extends` 后允许任意表达式
- 重写方法 Class 为此提供了
"super"
关键字。- 执行
super.method(...)
来调用一个父类方法。 - 执行
super(...)
来调用一个父类 constructor(只能在我们的 constructor 中)。
- 箭头函数没有
super
- 执行
- 重写 constructor
- 对于重写 constructor 来说,则有点棘手。
- 到目前为止,
Rabbit
还没有自己的constructor
。 - 继承类的 constructor 必须调用
super(...)
,并且 (!) 一定要在使用this
之前调用。 - 当通过new
执行一个常规函数时,它将创建一个空对象,并将这个空对象赋值给this
。 - 但是当继承的 constructor 执行时,它不会执行此操作。它期望父类的 constructor 来完成这项工作。 - 重写类字段: 一个棘手的注意要点
- 换句话说,父类构造器总是会使用它自己字段的值,而不是被重写的那一个。
- 深入:内部探究和
[[HomeObject]]
[[HomeObject]]
- 当一个函数被定义为类或者对象方法时,它的
[[HomeObject]]
属性就成为了该对象。 - 然后
super
使用它来解析(resolve)父原型及其方法。
- 当一个函数被定义为类或者对象方法时,它的
- 方法并不是“自由”的
[[HomeObject]]
的存在违反了这个原则,因为方法记住了它们的对象。[[HomeObject]]
不能被更改,所以这个绑定是永久的。
- 方法,不是函数属性
[[HomeObject]]
是为类和普通对象中的方法定义的。但是对于对象而言,方法必须确切指定为method()
,而不是"method: function()"
。
静态属性和静态方法
我们还可以为整个类分配一个方法。这样的方法被称为 静态的(static)。
JavaScript
class User {
static staticMethod() {
alert(this === User);
}
}
User.staticMethod(); // true
1. 静态方法不适用于单个对象
- 静态属性
- 静态的属性也是可能的,它们看起来就像常规的类属性,但前面加有
static
- 静态的属性也是可能的,它们看起来就像常规的类属性,但前面加有
- 继承静态属性和方法
- 静态属性和方法是可被继承的。
JavaScript
class Animal {
static planet = "Earth";
constructor(name, speed) {
this.speed = speed;
this.name = name;
}
run(speed = 0) {
this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
static compare(animalA, animalB) {
return animalA.speed - animalB.speed;
}
}
// 继承于 Animal
class Rabbit extends Animal {
hide() {
alert(`${this.name} hides!`);
}
}
let rabbits = [
new Rabbit("White Rabbit", 10),
new Rabbit("Black Rabbit", 5)
];
rabbits.sort(Rabbit.compare);
rabbits[0].run(); // Black Rabbit runs with speed 5.
alert(Rabbit.planet); // Earth
私有的和受保护的属性和方法
面向对象编程最重要的原则之一 —— 将内部接口与外部接口分隔开来。
- 一个现实生活中的例子
- 内部接口和外部接口
- 内部接口 —— 可以通过该类的其他方法访问,但不能从外部访问的方法和属性。
- 外部接口 —— 也可以从类的外部访问的方法和属性。
- 受保护的 “waterAmount”
- 受保护的属性通常以下划线
_
作为前缀。
- 受保护的属性通常以下划线
- 只读的 “power”
- 对于
power
属性,让我们将它设为只读。有时候一个属性必须只能被在创建时进行设置,之后不再被修改。 - getter/setter 函数
- 受保护的字段是可以被继承的
- 对于
- 私有的 “#waterLimit”
- 私有字段不能通过 this[name] 访问
扩展内建类
内建的类,例如 Array
,Map
等也都是可以扩展的(extendable)。 内建的方法例如 filter
,map
等 —— 返回的正是子类 PowerArray
的新对象。它们内部使用了对象的 constructor
属性来实现这一功能 当 arr.filter()
被调用时,它的内部使用的是 arr.constructor
来创建新的结果数组,而不是使用原生的 Array
。这真的很酷,因为我们可以在结果数组上继续使用 PowerArray
的方法。 我们可以给这个类添加一个特殊的静态 getter Symbol.species
,它会返回 JavaScript 在内部用来在 map
和 filter
等方法中创建新实体的 constructor
。
- 其他集合的工作方式类似
- 其他集合,例如
Map
和Set
的工作方式类似。它们也使用Symbol.species
。
- 其他集合,例如
- 内建类没有静态方法继承
- 内建对象有它们自己的静态方法,例如
Object.keys
,Array.isArray
等。 - 当一个类扩展另一个类时,静态方法和非静态方法都会被继承。这已经在 静态属性和静态方法 中详细地解释过了。
- 内建对象有它们自己的静态方法,例如
类检查:“instanceof”
instanceof
操作符用于检查一个对象是否属于某个特定的 class。同时,它还考虑了继承。 在许多情况下,可能都需要进行此类检查。例如,它可以被用来构建一个 多态性(polymorphic) 的函数,该函数根据参数的类型对参数进行不同的处理。
- instanceof 操作符
- obj
instanceof
Class
- 如果
obj
隶属于Class
类(或Class
类的衍生类),则返回true
。 - 如果这儿有静态方法
Symbol.hasInstance
,那就直接调用这个方法 - 大多数 class 没有
Symbol.hasInstance
。在这种情况下,标准的逻辑是:使用obj instanceOf Class
检查Class.prototype
是否等于obj
的原型链中的原型之一。
- obj
- 福利:使用 Object.prototype.toString 方法来揭示类型
- 大家都知道,一个普通对象被转化为字符串时为
[object Object]
- 对于 number 类型,结果是[object Number]
- 对于 boolean 类型,结果是[object Boolean]
- 对于null
:[object Null]
- 对于undefined
:[object Undefined]
- 对于数组:[object Array]
- ……等(可自定义)
- 大家都知道,一个普通对象被转化为字符串时为
- Symbol.toStringTag
- 可以使用特殊的对象属性
Symbol.toStringTag
自定义对象的toString
方法的行为。
- 可以使用特殊的对象属性
Mixin 模式
在 JavaScript 中,我们只能继承单个对象。每个对象只能有一个 [[Prototype]]
。并且每个类只可以扩展另外一个类。
- 一个 Mixin 实例
- 在 JavaScript 中构造一个 mixin 最简单的方式就是构造一个拥有实用方法的对象,以便我们可以轻松地将这些实用的方法合并到任何类的原型中。
- Mixin 可以在自己内部使用继承。
- EventMixin - Mixin 将提供
.trigger(name, [...data])
方法,以在发生重要的事情时“生成一个事件”。name
参数(arguments)是事件的名称,[...data]
是可选的带有事件数据的其他参数(arguments)。- 此外还有
.on(name, handler)
方法,它为具有给定名称的事件添加了handler
函数作为监听器(listener)。当具有给定name
的事件触发时将调用该方法,并从.trigger
调用中获取参数(arguments)。 - ……还有
.off(name, handler)
方法,它会删除handler
监听器(listener)。
- 此外还有