2025-01-23 日报 Day75

2025-01-23 日报 Day75

Yuyang 前端小白🥬

今日的鸡汤

生命因沉淀而厚重,人生因打磨更璀璨。从今天起静静蓄力,你想要的都将奔赴而来!

今日学习内容

1、JS 红皮书 P257-261 第八章:对象、类与面向对象编程

今日笔记

1、迭代器与生成器方法: 类定义语法支持在原型和类本身上定义生成器方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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

// 因为支持生成器方法,所以可以通过添加一个默认的迭代器,把类实例变成可迭代对象:
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
// 也可以只返回迭代器实例:
class Person {
constructor() {
this.nicknames = ["Jack", "Jake", "J-Dog"];
}
[Symbol.iterator]() {
return this.nicknames.entries();
}
}
let p = new Person();
for (let [idx, nickname] of p) {
console.log(nickname);
}
// Jack
// Jake
// J-Dog

2、继承: ECMAScript 6 新增特性中最出色的一个就是原生支持了类继承机制。虽然类继承使用的是新语法,但背后依旧使用的是原型链。

  • 继承基础: ES6 类支持单继承。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象。很大程度上,这意味着不仅可以继承一个类,也可以继承普通的构造函数(保持向后兼容):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
// 派生类都会通过原型链访问到类和原型上定义的方法。this 的值会反映调用相应方法的实例或者类:
class Vehicle {
identifyPrototype(id) {
console.log(id, this);
}
static identifyClass(id) {
console.log(id, this);
}
}
class Bus extends Vehicle {}
let v = new Vehicle();
let b = new Bus();
b.identifyPrototype("bus"); // bus, Bus {}
v.identifyPrototype("vehicle"); // vehicle, Vehicle {}
Bus.identifyClass("bus"); // bus, class Bus {}
Vehicle.identifyClass("vehicle"); // vehicle, class Vehicle {}

注意 extends 关键字也可以在类表达式中使用,因此 let Bar = class extends Foo {}是有效的语法。
2、构造函数、HomeObject 和 super(): 派生类的方法可以通过 super 关键字引用它们的原型。这个关键字只能在派生类中使用,而且仅限于类构造函数、实例方法和静态方法内部。在类构造函数中使用 super 可以调用父类构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Vehicle {
constructor() {
this.hasEngine = true;
}
}
class Bus extends Vehicle {
constructor() {
// 不要在调用 super()之前引用 this,否则会抛出 ReferenceError
super(); // 相当于 super.constructor()
console.log(this instanceof Vehicle); // true
console.log(this); // Bus { hasEngine: true }
}
}
new Bus();
// 在静态方法中可以通过 super 调用继承的类上定义的静态方法:
class Vehicle {
static identify() {
console.log("vehicle");
}
}
class Bus extends Vehicle {
static identify() {
super.identify();
}
}
Bus.identify(); // vehicle

注意 ES6 给类构造函数和静态方法添加了内部特性[[HomeObject]],这个特性是一个指针,指向定义该方法的对象。这个指针是自动赋值的,而且只能在 JavaScript 引擎内部访问。super 始终会定义为[[HomeObject]]的原型。
在使用 super 时要注意几个问题。
 super 只能在派生类构造函数和静态方法中使用。

1
2
3
4
5
6
class Vehicle {
constructor() {
super();
// SyntaxError: 'super' keyword unexpected
}
}

 不能单独引用 super 关键字,要么用它调用构造函数,要么用它引用静态方法。

1
2
3
4
5
6
7
class Vehicle {}
class Bus extends Vehicle {
constructor() {
console.log(super);
// SyntaxError: 'super' keyword unexpected here
}
}

 调用 super()会调用父类构造函数,并将返回的实例赋值给 this。

1
2
3
4
5
6
7
8
class Vehicle {}
class Bus extends Vehicle {
constructor() {
super();
console.log(this instanceof Vehicle);
}
}
new Bus(); // true

 super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入。

1
2
3
4
5
6
7
8
9
10
11
class Vehicle {
constructor(licensePlate) {
this.licensePlate = licensePlate;
}
}
class Bus extends Vehicle {
constructor(licensePlate) {
super(licensePlate);
}
}
console.log(new Bus('1337H4X')); // Bus { licensePlate: '1337H4X' }

 如果没有定义类构造函数,在实例化派生类时会调用 super(),而且会传入所有传给派生类的参数。

1
2
3
4
5
6
7
class Vehicle { 
constructor(licensePlate) {
this.licensePlate = licensePlate;
}
}
class Bus extends Vehicle {}
console.log(new Bus('1337H4X')); // Bus { licensePlate: '1337H4X' }

 在类构造函数中,不能在调用 super()之前引用 this。

1
2
3
4
5
6
7
8
9
class Vehicle {} 
class Bus extends Vehicle {
constructor() {
console.log(this);
}
}
new Bus();
// ReferenceError: Must call super constructor in derived class
// before accessing 'this' or returning from derived constructor

 如果在派生类中显式定义了构造函数,则要么必须在其中调用 super(),要么必须在其中返回一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Vehicle {} 
class Car extends Vehicle {}
class Bus extends Vehicle {
constructor() {
super();
}
}
class Van extends Vehicle {
constructor() {
return {};
}
}
console.log(new Car()); // Car {}
console.log(new Bus()); // Bus {}
console.log(new Van()); // {}
此页目录
2025-01-23 日报 Day75