OOP Three Principles

面向对象的三大特性:封装、继承、多态

问题

什么是面向对象的三大特性?用 JavaScript 如何实现?

解答

面向对象编程(OOP)的三大特性是:封装继承多态

1. 封装 (Encapsulation)

将数据和操作数据的方法绑定在一起,隐藏内部实现细节。

class BankAccount {
  // 私有属性(ES2022+)
  #balance = 0;

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  // 公开方法访问私有数据
  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
    }
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
      return amount;
    }
    return 0;
  }

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

const account = new BankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
// console.log(account.#balance); // SyntaxError: 无法直接访问私有属性

2. 继承 (Inheritance)

子类继承父类的属性和方法,实现代码复用。

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

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

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

  // 子类特有方法
  fetch() {
    console.log(`${this.name} is fetching`);
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // Buddy makes a sound(继承自父类)
dog.fetch(); // Buddy is fetching

3. 多态 (Polymorphism)

不同对象对同一方法有不同的实现。

class Animal {
  speak() {
    console.log('Animal speaks');
  }
}

class Dog extends Animal {
  speak() {
    console.log('Woof!');
  }
}

class Cat extends Animal {
  speak() {
    console.log('Meow!');
  }
}

// 多态:同一方法,不同行为
function makeAnimalSpeak(animal) {
  animal.speak();
}

const animals = [new Dog(), new Cat(), new Animal()];

animals.forEach(animal => makeAnimalSpeak(animal));
// Woof!
// Meow!
// Animal speaks

组合示例

// 形状基类
class Shape {
  getArea() {
    throw new Error('Must implement getArea');
  }
}

class Rectangle extends Shape {
  #width;
  #height;

  constructor(width, height) {
    super();
    this.#width = width;
    this.#height = height;
  }

  getArea() {
    return this.#width * this.#height;
  }
}

class Circle extends Shape {
  #radius;

  constructor(radius) {
    super();
    this.#radius = radius;
  }

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

// 多态计算总面积
function totalArea(shapes) {
  return shapes.reduce((sum, shape) => sum + shape.getArea(), 0);
}

const shapes = [new Rectangle(10, 5), new Circle(3)];
console.log(totalArea(shapes)); // 50 + 28.27... ≈ 78.27

关键点

  • 封装:用 # 声明私有属性,通过公开方法访问,保护内部状态
  • 继承extends 继承父类,super() 调用父类构造函数
  • 多态:子类重写父类方法,同一接口不同实现
  • JavaScript 通过原型链实现继承,ES6 class 是语法糖
  • 优先使用组合而非继承(Composition over Inheritance)