9JavaScript高级程序设计(第4版)

类

面向对象编程

class继承多态静态方法

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第9章:类(Classes)

一、导读

1.1 本章学习目标

第9章介绍ES6的class语法。本章核心目标:

  • 掌握class语法:构造函数、实例方法
  • 理解继承:extends和super
  • 掌握静态方法和属性
  • 学会使用私有字段:#前缀
  • 了解getter和setter

1.2 为什么本章如此重要

class是ES6引入的面向对象编程语法,虽然只是原型继承的语法糖,但让代码更加清晰和易于理解。现代JavaScript框架和库大量使用class。

1.3 核心问题引导

class和原型继承有什么本质关系?

super关键字在构造函数和普通方法中有什么不同?

私有字段为什么需要#前缀?

类的静态方法和实例方法有什么区别?


二、核心概念详解

9.1 类的基础

javascript
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
  
  static create(name, age) {
    return new Person(name, age);
  }
  
  toString() {
    return `${this.name} (${this.age})`;
  }
}

let p = new Person("Alice", 20);
p.greet();
console.log(Person.create("Bob", 25));

9.2 继承

javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // 必须先调用super
    this.breed = breed;
  }
  
  speak() {
    super.speak();  // 调用父类方法
    console.log(`${this.name} barks`);
  }
}

class GuideDog extends Dog {
  constructor(name, breed, owner) {
    super(name, breed);
    this.owner = owner;
  }
  
  guide() {
    console.log(`${this.name} is guiding ${this.owner}`);
  }
}

let dog = new GuideDog("Buddy", "Labrador", "Alice");
dog.speak();
dog.guide();

9.3 私有字段和方法

javascript
class BankAccount {
  #balance = 0;  // 私有字段
  #owner;
  
  constructor(owner, initialBalance = 0) {
    this.#owner = owner;
    this.#balance = initialBalance;
  }
  
  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      this.#logTransaction('deposit', amount);
    }
  }
  
  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
      this.#logTransaction('withdraw', amount);
      return true;
    }
    return false;
  }
  
  get balance() {
    return this.#balance;
  }
  
  #logTransaction(type, amount) {
    console.log(`${type}: ${amount}, balance: ${this.#balance}`);
  }
}

let account = new BankAccount("Alice", 1000);
account.deposit(500);
console.log(account.balance);  // 1500
// console.log(account.#balance);  // SyntaxError

9.4 类的其他特性

javascript
// getter和setter
class Circle {
  #radius;
  
  constructor(radius) {
    this.#radius = radius;
  }
  
  get radius() {
    return this.#radius;
  }
  
  set radius(value) {
    if (value < 0) throw new Error("Radius cannot be negative");
    this.#radius = value;
  }
  
  get area() {
    return Math.PI * this.#radius ** 2;
  }
}

let circle = new Circle(5);
console.log(circle.area);  // 78.54...

// 类的表达式形式
const MyClass = class {
  constructor() {}
};

// 计算属性名
const methodName = "greet";
class Greeter {
  [methodName]() {
    console.log("Hello!");
  }
}

// 字段初始化
class Config {
  theme = "light";
  language = "en";
  #privateField = "secret";
  
  static defaultTheme = "light";
  static #privateStatic = "private";
}

三、实战案例

3.1 事件系统

javascript
class EventEmitter {
  #listeners = new Map();
  
  on(event, callback) {
    if (!this.#listeners.has(event)) {
      this.#listeners.set(event, []);
    }
    this.#listeners.get(event).push(callback);
    return this;  // 链式调用
  }
  
  off(event, callback) {
    const listeners = this.#listeners.get(event);
    if (listeners) {
      this.#listeners.set(event, 
        listeners.filter(cb => cb !== callback));
    }
    return this;
  }
  
  emit(event, ...args) {
    const listeners = this.#listeners.get(event);
    if (listeners) {
      listeners.forEach(cb => cb(...args));
    }
    return this;
  }
  
  once(event, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.off(event, wrapper);
    };
    return this.on(event, wrapper);
  }
}

// 使用
const emitter = new EventEmitter();
emitter.on('data', (msg) => console.log(`Received: ${msg}`));
emitter.emit('data', 'Hello');

四、本章小结

第9章介绍了ES6的class语法:构造函数、继承(extends/super)、私有字段(#)、getter/setter、静态方法。class是原型继承的语法糖,但更清晰。


五、思考与练习

思考题

class和function constructor有什么本质区别?

为什么子类构造函数必须先调用super?

私有字段和Symbol模拟的私有属性有什么区别?

如何实现Mixin模式?

编程练习

实现一个简单的MVC框架(使用class)。

实现一个带中间件的请求处理器。

使用class实现一个观察者模式。


9/14类
0%