icon

ES6 引入了 class 语法,提供了更清晰的面向对象编程方式。class 本质上是基于原型的语法糖。

基本语法

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

const person = new Person('Alice', 25);
console.log(person.greet()); // "Hello, I'm Alice"

类特性

  1. 构造函数
    • constructor 方法在创建实例时调用
class Person {
  constructor(name) {
    this.name = name;
    console.log('Person created');
  }
}

const person = new Person('Alice'); // Person created
  1. 方法定义
    • 类方法定义在类的原型上
class Person {
  constructor(name) {
    this.name = name;
  }

  // 实例方法
  greet() {
    return `Hello, ${this.name}`;
  }

  // 静态方法
  static create(name) {
    return new Person(name);
  }
}

const person = new Person('Alice');
person.greet();           // 'Hello, Alice'
Person.create('Bob');     // 使用静态方法
  1. 静态方法
    • 使用 static 关键字定义,通过类调用
class MathUtils {
  static add(a, b) {
    return a + b;
  }

  static multiply(a, b) {
    return a * b;
  }
}

MathUtils.add(1, 2);        // 3
MathUtils.multiply(2, 3);   // 6
  1. 静态属性
    • ES2022 引入,使用 static 关键字
class Config {
  static apiUrl = 'https://api.example.com';
  static version = '1.0.0';
}

console.log(Config.apiUrl); // 'https://api.example.com'
  1. 私有字段
    • ES2022 引入,使用 # 前缀
class BankAccount {
  #balance = 0; // 私有字段

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
// account.#balance; // SyntaxError: Private field
  1. getter 和 setter
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  set fullName(name) {
    [this.firstName, this.lastName] = name.split(' ');
  }
}

const person = new Person('Alice', 'Smith');
console.log(person.fullName); // 'Alice Smith'
person.fullName = 'Bob Jones';
console.log(person.firstName); // 'Bob'

继承

使用 extends 关键字实现继承:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }

  speak() {
    return `${this.name} barks`;
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.speak()); // 'Buddy barks'
console.log(dog.name);    // 'Buddy'

super 关键字

  • 调用父类构造函数:super(...args)
  • 调用父类方法:super.methodName()
class Animal {
  constructor(name) {
    this.name = name;
  }

  move() {
    return `${this.name} moves`;
  }
}

class Bird extends Animal {
  constructor(name, canFly) {
    super(name);
    this.canFly = canFly;
  }

  move() {
    return this.canFly 
      ? `${this.name} flies`
      : super.move(); // 调用父类方法
  }
}

const bird = new Bird('Eagle', true);
console.log(bird.move()); // 'Eagle flies'

类表达式

// 命名类表达式
const Person = class NamedPerson {
  constructor(name) {
    this.name = name;
  }
};

// 匿名类表达式
const Animal = class {
  constructor(name) {
    this.name = name;
  }
};

实际应用

  1. 单例模式
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
    return this;
  }
}

const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
  1. 工厂模式
class AnimalFactory {
  static create(type, name) {
    switch (type) {
      case 'dog':
        return new Dog(name);
      case 'cat':
        return new Cat(name);
      default:
        throw new Error('Unknown animal type');
    }
  }
}

const dog = AnimalFactory.create('dog', 'Buddy');
  1. Mixin 模式
const Flyable = {
  fly() {
    return `${this.name} is flying`;
  }
};

const Swimmable = {
  swim() {
    return `${this.name} is swimming`;
  }
};

class Duck extends Animal {
  constructor(name) {
    super(name);
    Object.assign(this, Flyable, Swimmable);
  }
}

const duck = new Duck('Donald');
console.log(duck.fly());  // 'Donald is flying'
console.log(duck.swim()); // 'Donald is swimming'
  1. 抽象类(模拟)
class AbstractShape {
  constructor() {
    if (new.target === AbstractShape) {
      throw new Error('Cannot instantiate abstract class');
    }
  }

  getArea() {
    throw new Error('Method must be implemented');
  }
}

class Circle extends AbstractShape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  getArea() {
    return Math.PI * this.radius ** 2;
  }
}

const circle = new Circle(5);
console.log(circle.getArea()); // 78.54

与构造函数对比

// ES5 构造函数
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `Hello, ${this.name}`;
};

// ES6 类
class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hello, ${this.name}`;
  }
}

注意事项

  1. 类不会提升
const person = new Person('Alice'); // ReferenceError

class Person {
  constructor(name) {
    this.name = name;
  }
}
  1. 类体在严格模式下运行
class Person {
  constructor(name) {
    this.name = name; // 自动严格模式
  }
}
  1. 类方法不可枚举
class Person {
  greet() {}
}

const person = new Person();
console.log(Object.keys(person)); // [] (方法不可枚举)
  1. 必须使用 new 调用
class Person {
  constructor(name) {
    this.name = name;
  }
}

Person('Alice'); // TypeError: Class constructor cannot be invoked without 'new'
  1. 类名在类内部是常量
class Person {
  constructor() {
    Person = 'changed'; // TypeError: Assignment to constant variable
  }
}

类字段(ES2022)

class Person {
  // 公共字段
  name = 'Unknown';
  age = 0;

  // 私有字段
  #privateField = 'private';

  // 静态公共字段
  static version = '1.0.0';

  // 静态私有字段
  static #privateStaticField = 'private static';

  constructor(name) {
    this.name = name;
  }
}

与原型的关系

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hello, ${this.name}`;
  }
}

// 等价于
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `Hello, ${this.name}`;
};

// 验证
const person = new Person('Alice');
console.log(person.__proto__ === Person.prototype); // true
console.log(person instanceof Person); // true