面向对象编程思想
OOP 的四大特性及 JavaScript 实现
问题
什么是面向对象编程(OOP)?它有哪些特性?在 JavaScript 中如何实现?
解答
面向对象编程是一种以”对象”为中心的编程范式,通过封装、继承、多态、抽象来组织代码。
1. 封装
将数据和操作数据的方法绑定在一起,隐藏内部实现细节。
class BankAccount {
#balance = 0; // 私有属性
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();
account.deposit(100);
account.withdraw(30);
console.log(account.getBalance()); // 70
// console.log(account.#balance); // 报错,无法直接访问私有属性
2. 继承
子类继承父类的属性和方法,实现代码复用。
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;
}
speak() {
console.log(`${this.name} barks`);
}
fetch() {
console.log(`${this.name} fetches the ball`);
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // Buddy barks
dog.fetch(); // Buddy fetches the ball
3. 多态
不同对象对同一方法有不同的实现。
class Shape {
getArea() {
throw new Error('子类必须实现 getArea 方法');
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
getArea() {
return Math.PI * this.radius ** 2;
}
}
// 同一个方法,不同的行为
function printArea(shape) {
console.log(shape.getArea());
}
printArea(new Rectangle(4, 5)); // 20
printArea(new Circle(3)); // 28.274...
4. 抽象
提取共同特征,隐藏复杂实现。JavaScript 没有原生抽象类,可以模拟实现。
class Database {
constructor() {
if (new.target === Database) {
throw new Error('Database 是抽象类,不能直接实例化');
}
}
// 抽象方法
connect() {
throw new Error('子类必须实现 connect 方法');
}
query(sql) {
throw new Error('子类必须实现 query 方法');
}
}
class MySQL extends Database {
connect() {
console.log('Connected to MySQL');
}
query(sql) {
console.log(`MySQL executing: ${sql}`);
}
}
class MongoDB extends Database {
connect() {
console.log('Connected to MongoDB');
}
query(sql) {
console.log(`MongoDB executing: ${sql}`);
}
}
// const db = new Database(); // 报错
const mysql = new MySQL();
mysql.connect(); // Connected to MySQL
mysql.query('SELECT * FROM users'); // MySQL executing: SELECT * FROM users
关键点
- 封装:使用
#定义私有属性,通过公共方法访问 - 继承:
extends继承父类,super调用父类构造函数和方法 - 多态:子类重写父类方法,同一接口不同实现
- 抽象:通过
new.target和抛出错误模拟抽象类 - 优势:提高代码复用性、可维护性和可扩展性
目录