JavaScript类与面向对象编程
JavaScript虽然是一种基于原型的语言,但ES6引入的class语法为面向对象编程提供了更直观的方式。本文将深入探讨JavaScript中的类与面向对象编程概念和实践。
面向对象编程基础
什么是面向对象编程
面向对象编程(OOP)是一种编程范式,它将现实世界的概念抽象为对象,强调:
- 封装:将数据和操作数据的方法组合在一起
- 继承:子类可以继承父类的属性和方法
- 多态:同一接口可以有不同的实现
- 抽象:隐藏复杂的实现细节,只暴露必要的接口
面向对象与函数式编程的对比
面向对象编程关注"对象是什么",而函数式编程关注"做什么":
javascript
// 面向对象风格:计算器类
class Calculator {
constructor() {
this.result = 0;
}
add(value) {
this.result += value;
return this;
}
multiply(value) {
this.result *= value;
return this;
}
getResult() {
return this.result;
}
}
const calc = new Calculator();
const result = calc.add(5).multiply(2).getResult(); // 10
// 函数式风格:纯函数组合
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
const calculate = pipe(
x => add(x, 5),
x => multiply(x, 2)
);
const result = calculate(0); // 10
类的基础语法
类声明与类表达式
JavaScript提供了两种定义类的方式:
javascript
// 类声明
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `你好,我是${this.name},今年${this.age}岁`;
}
}
// 类表达式
const Animal = class {
constructor(species) {
this.species = species;
}
makeSound() {
return `${this.species}发出了声音`;
}
};
// 命名类表达式
const Vehicle = class Car {
constructor(brand) {
this.brand = brand;
}
start() {
return `${this.brand}汽车启动了`;
}
};
类声明与函数声明的区别:
- 类声明不会被提升(hoisting)
- 类声明会自动启用严格模式
- 类的方法不可枚举
构造函数与实例化
构造函数是类的特殊方法,用于创建和初始化对象实例:
javascript
class User {
constructor(username, email) {
// 实例属性
this.username = username;
this.email = email;
this.createdAt = new Date();
this.isActive = true;
// 参数验证
if (!username || !email) {
throw new Error('用户名和邮箱不能为空');
}
// 初始化逻辑
this.id = this.generateId();
}
generateId() {
return Math.random().toString(36).substr(2, 9);
}
getProfile() {
return {
id: this.id,
username: this.username,
email: this.email,
createdAt: this.createdAt,
isActive: this.isActive
};
}
}
// 创建实例
const user1 = new User('张三', 'zhangsan@example.com');
const user2 = new User('李四', 'lisi@example.com');
console.log(user1.getProfile());
console.log(user2.getProfile());
实例方法与原型
类中定义的方法会被添加到类的原型上:
javascript
class Counter {
constructor(initialValue = 0) {
this.value = initialValue;
}
// 实例方法
increment() {
this.value++;
return this;
}
decrement() {
this.value--;
return this;
}
reset() {
this.value = 0;
return this;
}
getValue() {
return this.value;
}
}
const counter = new Counter(5);
// 方法链调用
counter.increment().increment().decrement();
console.log(counter.getValue()); // 6
// 验证方法在原型上
console.log(Counter.prototype.increment); // [Function: increment]
console.log(counter.hasOwnProperty('increment')); // false
console.log(counter.hasOwnProperty('value')); // true
类的高级特性
静态方法与静态属性
静态成员属于类本身,而不是类的实例:
javascript
class MathUtils {
// 静态属性
static PI = 3.14159;
static E = 2.71828;
// 静态方法
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
static circleArea(radius) {
return this.PI * radius * radius;
}
static factorial(n) {
if (n <= 1) return 1;
return n * this.factorial(n - 1);
}
}
// 使用静态成员
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.circleArea(5)); // 78.53975
console.log(MathUtils.factorial(5)); // 120
// 静态方法不能通过实例调用
const math = new MathUtils();
// console.log(math.add(1, 2)); // TypeError: math.add is not a function
实际应用场景:
javascript
class User {
constructor(username, email) {
this.username = username;
this.email = email;
this.id = User.generateId();
}
// 静态方法:工厂方法
static createAdmin(username, email) {
const admin = new User(username, email);
admin.role = 'admin';
admin.permissions = ['read', 'write', 'delete'];
return admin;
}
static createGuest() {
const guest = new User('guest', 'guest@example.com');
guest.role = 'guest';
guest.permissions = ['read'];
return guest;
}
// 静态方法:工具方法
static generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
static validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// 静态方法:查找方法
static findById(users, id) {
return users.find(user => user.id === id);
}
}
// 使用工厂方法
const admin = User.createAdmin('admin', 'admin@example.com');
const guest = User.createGuest();
console.log(admin.role); // 'admin'
console.log(guest.permissions); // ['read']
// 使用工具方法
console.log(User.validateEmail('test@example.com')); // true
console.log(User.validateEmail('invalid-email')); // false
访问器属性(Getter/Setter)
访问器属性允许我们定义看起来像属性但实际上是方法的成员:
javascript
class Temperature {
constructor(celsius = 0) {
this._celsius = celsius;
}
// Getter:获取摄氏度
get celsius() {
return this._celsius;
}
// Setter:设置摄氏度
set celsius(value) {
if (typeof value !== 'number') {
throw new Error('温度必须是数字');
}
if (value < -273.15) {
throw new Error('温度不能低于绝对零度');
}
this._celsius = value;
}
// Getter:获取华氏度
get fahrenheit() {
return this._celsius * 9/5 + 32;
}
// Setter:设置华氏度
set fahrenheit(value) {
this.celsius = (value - 32) * 5/9;
}
// Getter:获取开尔文温度
get kelvin() {
return this._celsius + 273.15;
}
// Setter:设置开尔文温度
set kelvin(value) {
this.celsius = value - 273.15;
}
toString() {
return `${this._celsius}°C (${this.fahrenheit}°F, ${this.kelvin}K)`;
}
}
const temp = new Temperature(25);
console.log(temp.toString()); // "25°C (77°F, 298.15K)"
// 通过不同单位设置温度
temp.fahrenheit = 100;
console.log(temp.celsius); // 37.77777777777778
temp.kelvin = 300;
console.log(temp.celsius); // 26.850000000000023
访问器的实际应用:
javascript
class BankAccount {
constructor(accountNumber, initialBalance = 0) {
this.accountNumber = accountNumber;
this._balance = initialBalance;
this._transactions = [];
}
// 只读属性
get balance() {
return this._balance;
}
get transactions() {
return [...this._transactions]; // 返回副本,防止外部修改
}
// 计算属性
get totalDeposits() {
return this._transactions
.filter(t => t.type === 'deposit')
.reduce((sum, t) => sum + t.amount, 0);
}
get totalWithdrawals() {
return this._transactions
.filter(t => t.type === 'withdrawal')
.reduce((sum, t) => sum + t.amount, 0);
}
deposit(amount) {
if (amount <= 0) {
throw new Error('存款金额必须大于0');
}
this._balance += amount;
this._transactions.push({
type: 'deposit',
amount,
date: new Date(),
balance: this._balance
});
}
withdraw(amount) {
if (amount <= 0) {
throw new Error('取款金额必须大于0');
}
if (amount > this._balance) {
throw new Error('余额不足');
}
this._balance -= amount;
this._transactions.push({
type: 'withdrawal',
amount,
date: new Date(),
balance: this._balance
});
}
}
const account = new BankAccount('123456789', 1000);
account.deposit(500);
account.withdraw(200);
console.log(account.balance); // 1300
console.log(account.totalDeposits); // 500
console.log(account.totalWithdrawals); // 200
私有字段和方法
现代浏览器支持真正的私有字段和方法,使用#
前缀:
javascript
class SecureWallet {
// 私有字段
#balance = 0;
#pin;
#attempts = 0;
#maxAttempts = 3;
#locked = false;
constructor(initialBalance, pin) {
this.#balance = initialBalance;
this.#pin = pin;
}
// 私有方法
#validatePin(inputPin) {
if (this.#locked) {
throw new Error('钱包已被锁定');
}
if (inputPin !== this.#pin) {
this.#attempts++;
if (this.#attempts >= this.#maxAttempts) {
this.#locked = true;
throw new Error('钱包已被锁定,尝试次数过多');
}
throw new Error(`PIN错误,还有${this.#maxAttempts - this.#attempts}次机会`);
}
this.#attempts = 0; // 重置尝试次数
}
#log(action, amount) {
console.log(`[${new Date().toISOString()}] ${action}: ${amount}, 余额: ${this.#balance}`);
}
// 公有方法
getBalance(pin) {
this.#validatePin(pin);
return this.#balance;
}
deposit(amount, pin) {
this.#validatePin(pin);
if (amount <= 0) {
throw new Error('存款金额必须大于0');
}
this.#balance += amount;
this.#log('存款', amount);
}
withdraw(amount, pin) {
this.#validatePin(pin);
if (amount <= 0) {
throw new Error('取款金额必须大于0');
}
if (amount > this.#balance) {
throw new Error('余额不足');
}
this.#balance -= amount;
this.#log('取款', amount);
}
changePin(oldPin, newPin) {
this.#validatePin(oldPin);
this.#pin = newPin;
console.log('PIN已更改');
}
unlock(masterKey) {
if (masterKey === 'MASTER_RESET_2024') {
this.#locked = false;
this.#attempts = 0;
console.log('钱包已解锁');
} else {
throw new Error('主密钥错误');
}
}
}
const wallet = new SecureWallet(1000, '1234');
// 正常操作
wallet.deposit(500, '1234');
console.log(wallet.getBalance('1234')); // 1500
// 私有字段无法从外部访问
// console.log(wallet.#balance); // SyntaxError
try {
wallet.getBalance('0000');
} catch (error) {
console.log(error.message);
}
私有字段的特点:
- 只能在类内部访问
- 不会被继承
- 不能动态添加
- 提供真正的封装
类字段声明
现代JavaScript允许直接在类体中声明字段:
javascript
class GameCharacter {
// 公有字段
name = '未命名角色';
level = 1;
experience = 0;
// 私有字段
#health = 100;
#maxHealth = 100;
#mana = 50;
#maxMana = 50;
// 静态字段
static maxLevel = 100;
static experienceTable = [0, 100, 300, 600, 1000];
constructor(name, characterClass = 'warrior') {
this.name = name;
this.characterClass = characterClass;
this.skills = this.#getInitialSkills(characterClass);
}
#getInitialSkills(characterClass) {
const skillSets = {
warrior: ['剑术', '盾牌防御'],
mage: ['火球术', '治疗术'],
archer: ['精准射击', '快速移动']
};
return skillSets[characterClass] || skillSets.warrior;
}
// Getter for health percentage
get healthPercentage() {
return (this.#health / this.#maxHealth) * 100;
}
get isAlive() {
return this.#health > 0;
}
takeDamage(damage) {
this.#health = Math.max(0, this.#health - damage);
console.log(`${this.name}受到${damage}点伤害,剩余生命值:${this.#health}`);
if (!this.isAlive) {
console.log(`${this.name}已死亡`);
}
}
heal(amount) {
this.#health = Math.min(this.#maxHealth, this.#health + amount);
console.log(`${this.name}恢复${amount}点生命值,当前生命值:${this.#health}`);
}
gainExperience(exp) {
this.experience += exp;
console.log(`${this.name}获得${exp}点经验值`);
// 检查是否升级
while (this.level < GameCharacter.maxLevel &&
this.experience >= GameCharacter.experienceTable[this.level]) {
this.levelUp();
}
}
levelUp() {
this.level++;
this.#maxHealth += 20;
this.#health = this.#maxHealth; // 升级时恢复满血
this.#maxMana += 10;
this.#mana = this.#maxMana;
console.log(`🎉 ${this.name}升级到${this.level}级!`);
}
getStatus() {
return {
name: this.name,
class: this.characterClass,
level: this.level,
experience: this.experience,
health: `${this.#health}/${this.#maxHealth}`,
mana: `${this.#mana}/${this.#maxMana}`,
skills: this.skills,
isAlive: this.isAlive
};
}
}
const hero = new GameCharacter('亚瑟', 'warrior');
console.log(hero.getStatus());
hero.gainExperience(150);
hero.takeDamage(30);
hero.heal(10);
私有字段注意事项:
- 需要现代浏览器支持(Chrome 74+, Firefox 90+)
- Node.js 12+ 支持
- 老版本浏览器需要Babel转译
## 继承与多态
### 基础继承
使用`extends`关键字实现类的继承:
```javascript
// 基类
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
this.energy = 100;
}
eat(food) {
this.energy += 20;
console.log(`${this.name}吃了${food},能量恢复到${this.energy}`);
}
sleep() {
this.energy = 100;
console.log(`${this.name}睡了一觉,精力充沛`);
}
makeSound() {
console.log(`${this.name}发出了声音`);
}
getInfo() {
return `${this.name}是一只${this.species},当前能量:${this.energy}`;
}
}
// 子类:狗
class Dog extends Animal {
constructor(name, breed) {
super(name, '狗'); // 调用父类构造函数
this.breed = breed;
this.loyalty = 100;
}
// 重写父类方法
makeSound() {
console.log(`${this.name}汪汪叫`);
}
// 新增方法
fetch() {
if (this.energy < 20) {
console.log(`${this.name}太累了,需要休息`);
return;
}
this.energy -= 20;
console.log(`${this.name}去捡球了,能量降到${this.energy}`);
}
wagTail() {
console.log(`${this.name}开心地摇尾巴`);
}
// 重写父类方法,添加额外逻辑
getInfo() {
return `${super.getInfo()},品种:${this.breed},忠诚度:${this.loyalty}`;
}
}
// 子类:猫
class Cat extends Animal {
constructor(name, color) {
super(name, '猫');
this.color = color;
this.independence = 80;
}
makeSound() {
console.log(`${this.name}喵喵叫`);
}
climb() {
if (this.energy < 15) {
console.log(`${this.name}太累了,不想爬树`);
return;
}
this.energy -= 15;
console.log(`${this.name}爬到了树上,能量降到${this.energy}`);
}
purr() {
console.log(`${this.name}满足地呼噜呼噜`);
}
getInfo() {
return `${super.getInfo()},颜色:${this.color},独立性:${this.independence}`;
}
}
// 使用示例
const dog = new Dog('旺财', '金毛');
const cat = new Cat('咪咪', '橘色');
// 多态示例
const animals = [dog, cat];
animals.forEach(animal => {
animal.makeSound();
console.log(animal.getInfo());
});
dog.fetch();
dog.wagTail();
cat.climb();
cat.purr();
抽象类模拟
JavaScript没有内置的抽象类,但可以通过约定和错误抛出来模拟:
javascript
// 抽象基类
class Shape {
constructor(color) {
if (new.target === Shape) {
throw new Error('Shape是抽象类,不能直接实例化');
}
this.color = color;
}
// 抽象方法:子类必须实现
getArea() {
throw new Error('getArea方法必须在子类中实现');
}
getPerimeter() {
throw new Error('getPerimeter方法必须在子类中实现');
}
// 具体方法:子类可以直接使用
getColor() {
return this.color;
}
setColor(color) {
this.color = color;
}
describe() {
return `这是一个${this.color}的图形,面积为${this.getArea()},周长为${this.getPerimeter()}`;
}
}
// 具体子类:圆形
class Circle extends Shape {
constructor(radius, color) {
super(color);
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
getPerimeter() {
return 2 * Math.PI * this.radius;
}
}
// 具体子类:矩形
class Rectangle extends Shape {
constructor(width, height, color) {
super(color);
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
getPerimeter() {
return 2 * (this.width + this.height);
}
}
// 使用示例
const circle = new Circle(5, '红色');
const rectangle = new Rectangle(4, 6, '蓝色');
console.log(circle.describe());
console.log(rectangle.describe());
// 直接实例化抽象类会报错
// const shape = new Shape('绿色'); // Error
接口模拟
JavaScript可以通过混入(Mixin)模式模拟接口:
javascript
// 定义接口(混入)
const Flyable = {
fly() {
console.log(`${this.name}在飞行`);
},
land() {
console.log(`${this.name}降落了`);
}
};
const Swimmable = {
swim() {
console.log(`${this.name}在游泳`);
},
dive() {
console.log(`${this.name}潜水了`);
}
};
const Walkable = {
walk() {
console.log(`${this.name}在走路`);
},
run() {
console.log(`${this.name}在跑步`);
}
};
// 混入函数
function mixin(target, ...sources) {
sources.forEach(source => {
Object.getOwnPropertyNames(source).forEach(name => {
if (name !== 'constructor') {
target.prototype[name] = source[name];
}
});
});
}
// 基类
class Animal {
constructor(name) {
this.name = name;
}
}
// 鸟类:可以飞行和走路
class Bird extends Animal {
constructor(name, species) {
super(name);
this.species = species;
}
}
mixin(Bird, Flyable, Walkable);
// 鱼类:可以游泳
class Fish extends Animal {
constructor(name, species) {
super(name);
this.species = species;
}
}
mixin(Fish, Swimmable);
// 鸭子:可以飞行、游泳和走路
class Duck extends Animal {
constructor(name) {
super(name);
this.species = '鸭子';
}
}
mixin(Duck, Flyable, Swimmable, Walkable);
// 使用示例
const eagle = new Bird('老鹰', '猛禽');
const salmon = new Fish('三文鱼', '鲑鱼');
const duck = new Duck('唐老鸭');
eagle.fly();
eagle.walk();
salmon.swim();
salmon.dive();
duck.fly();
duck.swim();
duck.walk();
设计模式与类
单例模式
确保一个类只有一个实例:
javascript
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.host = 'localhost';
this.port = 5432;
this.database = 'myapp';
this.connected = false;
DatabaseConnection.instance = this;
}
connect() {
if (!this.connected) {
console.log(`连接到数据库 ${this.database}@${this.host}:${this.port}`);
this.connected = true;
}
return this;
}
disconnect() {
if (this.connected) {
console.log('断开数据库连接');
this.connected = false;
}
return this;
}
query(sql) {
if (!this.connected) {
throw new Error('数据库未连接');
}
console.log(`执行查询: ${sql}`);
return { rows: [], count: 0 };
}
static getInstance() {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
}
// 使用示例
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
const db3 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true,同一个实例
console.log(db2 === db3); // true
db1.connect().query('SELECT * FROM users');
工厂模式
根据参数创建不同类型的对象:
javascript
// 产品基类
class Vehicle {
constructor(make, model) {
this.make = make;
this.model = model;
}
start() {
console.log(`${this.make} ${this.model} 启动了`);
}
stop() {
console.log(`${this.make} ${this.model} 停止了`);
}
}
// 具体产品类
class Car extends Vehicle {
constructor(make, model, doors) {
super(make, model);
this.doors = doors;
this.type = 'car';
}
openTrunk() {
console.log('后备箱打开了');
}
}
class Motorcycle extends Vehicle {
constructor(make, model, engineSize) {
super(make, model);
this.engineSize = engineSize;
this.type = 'motorcycle';
}
wheelie() {
console.log('摩托车翘起前轮');
}
}
class Truck extends Vehicle {
constructor(make, model, capacity) {
super(make, model);
this.capacity = capacity;
this.type = 'truck';
}
loadCargo() {
console.log(`装载货物,容量:${this.capacity}吨`);
}
}
// 工厂类
class VehicleFactory {
static createVehicle(type, make, model, extra) {
switch (type.toLowerCase()) {
case 'car':
return new Car(make, model, extra);
case 'motorcycle':
return new Motorcycle(make, model, extra);
case 'truck':
return new Truck(make, model, extra);
default:
throw new Error(`不支持的车辆类型: ${type}`);
}
}
// 注册新的车辆类型
static registerVehicleType(type, vehicleClass) {
this.vehicleTypes = this.vehicleTypes || {};
this.vehicleTypes[type.toLowerCase()] = vehicleClass;
}
static createRegisteredVehicle(type, ...args) {
const VehicleClass = this.vehicleTypes?.[type.toLowerCase()];
if (!VehicleClass) {
throw new Error(`未注册的车辆类型: ${type}`);
}
return new VehicleClass(...args);
}
}
// 使用示例
const car = VehicleFactory.createVehicle('car', '丰田', '卡罗拉', 4);
const motorcycle = VehicleFactory.createVehicle('motorcycle', '本田', 'CBR600', 600);
const truck = VehicleFactory.createVehicle('truck', '沃尔沃', 'FH16', 40);
car.start();
car.openTrunk();
motorcycle.start();
motorcycle.wheelie();
truck.start();
truck.loadCargo();
// 注册新类型
class Bicycle extends Vehicle {
constructor(make, model, gears) {
super(make, model);
this.gears = gears;
this.type = 'bicycle';
}
pedal() {
console.log('开始踩踏板');
}
}
VehicleFactory.registerVehicleType('bicycle', Bicycle);
const bike = VehicleFactory.createRegisteredVehicle('bicycle', '捷安特', 'ATX', 21);
bike.pedal();
观察者模式
实现对象间的一对多依赖关系:
javascript
// 观察者接口
class Observer {
update(data) {
throw new Error('update方法必须在子类中实现');
}
}
// 主题(被观察者)
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
if (!(observer instanceof Observer)) {
throw new Error('观察者必须实现Observer接口');
}
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
notifyObservers(data) {
this.observers.forEach(observer => observer.update(data));
}
}
// 具体主题:新闻发布者
class NewsPublisher extends Subject {
constructor() {
super();
this.news = [];
}
publishNews(title, content, category) {
const newsItem = {
id: Date.now(),
title,
content,
category,
publishTime: new Date()
};
this.news.push(newsItem);
this.notifyObservers(newsItem);
}
getNews() {
return [...this.news];
}
}
// 具体观察者:新闻订阅者
class NewsSubscriber extends Observer {
constructor(name, interests = []) {
super();
this.name = name;
this.interests = interests;
this.receivedNews = [];
}
update(newsItem) {
// 如果没有指定兴趣或新闻类别在兴趣范围内
if (this.interests.length === 0 || this.interests.includes(newsItem.category)) {
this.receivedNews.push(newsItem);
console.log(`${this.name}收到新闻: ${newsItem.title}`);
}
}
getReceivedNews() {
return [...this.receivedNews];
}
}
// 具体观察者:邮件通知服务
class EmailNotificationService extends Observer {
constructor() {
super();
this.sentEmails = [];
}
update(newsItem) {
const email = {
to: 'subscribers@news.com',
subject: `新闻快报: ${newsItem.title}`,
body: newsItem.content,
sentAt: new Date()
};
this.sentEmails.push(email);
console.log(`邮件通知已发送: ${newsItem.title}`);
}
getSentEmails() {
return [...this.sentEmails];
}
}
// 使用示例
const publisher = new NewsPublisher();
const subscriber1 = new NewsSubscriber('张三', ['科技', '体育']);
const subscriber2 = new NewsSubscriber('李四', ['财经']);
const subscriber3 = new NewsSubscriber('王五'); // 订阅所有类别
const emailService = new EmailNotificationService();
// 添加观察者
publisher.addObserver(subscriber1);
publisher.addObserver(subscriber2);
publisher.addObserver(subscriber3);
publisher.addObserver(emailService);
// 发布新闻
publisher.publishNews('AI技术突破', 'ChatGPT发布新版本...', '科技');
publisher.publishNews('股市大涨', '今日股市表现强劲...', '财经');
publisher.publishNews('世界杯决赛', '精彩的足球比赛...', '体育');
console.log('\n订阅者收到的新闻数量:');
console.log(`张三: ${subscriber1.getReceivedNews().length}条`);
console.log(`李四: ${subscriber2.getReceivedNews().length}条`);
console.log(`王五: ${subscriber3.getReceivedNews().length}条`);
装饰器模式
动态地给对象添加新功能:
javascript
// 基础组件
class Coffee {
constructor() {
this.description = '普通咖啡';
this.cost = 10;
}
getDescription() {
return this.description;
}
getCost() {
return this.cost;
}
}
// 装饰器基类
class CoffeeDecorator {
constructor(coffee) {
this.coffee = coffee;
}
getDescription() {
return this.coffee.getDescription();
}
getCost() {
return this.coffee.getCost();
}
}
// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
constructor(coffee) {
super(coffee);
}
getDescription() {
return this.coffee.getDescription() + ', 加牛奶';
}
getCost() {
return this.coffee.getCost() + 3;
}
}
class SugarDecorator extends CoffeeDecorator {
constructor(coffee) {
super(coffee);
}
getDescription() {
return this.coffee.getDescription() + ', 加糖';
}
getCost() {
return this.coffee.getCost() + 1;
}
}
class WhipDecorator extends CoffeeDecorator {
constructor(coffee) {
super(coffee);
}
getDescription() {
return this.coffee.getDescription() + ', 加奶泡';
}
getCost() {
return this.coffee.getCost() + 5;
}
}
// 使用示例
let coffee = new Coffee();
console.log(`${coffee.getDescription()} - ¥${coffee.getCost()}`);
// 添加装饰
coffee = new MilkDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.getCost()}`);
coffee = new SugarDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.getCost()}`);
coffee = new WhipDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.getCost()}`);
// 最终输出: 普通咖啡, 加牛奶, 加糖, 加奶泡 - ¥19
类与现代JavaScript
类与模块化
javascript
// user.js - 用户模块
export class User {
constructor(username, email) {
this.username = username;
this.email = email;
this.createdAt = new Date();
}
getProfile() {
return {
username: this.username,
email: this.email,
createdAt: this.createdAt
};
}
}
export class AdminUser extends User {
constructor(username, email, permissions = []) {
super(username, email);
this.permissions = permissions;
this.role = 'admin';
}
hasPermission(permission) {
return this.permissions.includes(permission);
}
addPermission(permission) {
if (!this.hasPermission(permission)) {
this.permissions.push(permission);
}
}
}
// userManager.js - 用户管理模块
import { User, AdminUser } from './user.js';
export class UserManager {
constructor() {
this.users = new Map();
}
createUser(username, email, isAdmin = false, permissions = []) {
const user = isAdmin
? new AdminUser(username, email, permissions)
: new User(username, email);
this.users.set(username, user);
return user;
}
getUser(username) {
return this.users.get(username);
}
getAllUsers() {
return Array.from(this.users.values());
}
deleteUser(username) {
return this.users.delete(username);
}
}
// main.js - 主应用
import { UserManager } from './userManager.js';
const userManager = new UserManager();
const regularUser = userManager.createUser('john', 'john@example.com');
const admin = userManager.createUser('admin', 'admin@example.com', true, ['read', 'write', 'delete']);
console.log(regularUser.getProfile());
console.log(admin.hasPermission('delete')); // true
类与异步编程
javascript
class DataService {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.cache = new Map();
}
async fetchData(endpoint) {
const url = `${this.baseUrl}/${endpoint}`;
// 检查缓存
if (this.cache.has(url)) {
console.log('从缓存获取数据');
return this.cache.get(url);
}
try {
console.log(`正在获取数据: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
const data = await response.json();
// 缓存数据
this.cache.set(url, data);
return data;
} catch (error) {
console.error('获取数据失败:', error);
throw error;
}
}
async batchFetch(endpoints) {
const promises = endpoints.map(endpoint => this.fetchData(endpoint));
return Promise.all(promises);
}
clearCache() {
this.cache.clear();
}
}
// 使用示例
const dataService = new DataService('https://api.example.com');
async function loadUserData() {
try {
const userData = await dataService.fetchData('users/123');
const userPosts = await dataService.fetchData('users/123/posts');
console.log('用户数据:', userData);
console.log('用户帖子:', userPosts);
} catch (error) {
console.error('加载用户数据失败:', error);
}
}
最佳实践
类设计原则
单一职责原则
每个类应该只负责一件事:
javascript
// 不好的设计:一个类承担多个职责
class BadUser {
constructor(username, email) {
this.username = username;
this.email = email;
}
// 用户数据验证
validateEmail() {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
}
// 数据库操作
save() {
// 保存到数据库的逻辑
}
// 邮件发送
sendWelcomeEmail() {
// 发送欢迎邮件的逻辑
}
// 日志记录
log(message) {
console.log(`[${new Date()}] ${message}`);
}
}
// 好的设计:职责分离
class User {
constructor(username, email) {
this.username = username;
this.email = email;
}
getProfile() {
return {
username: this.username,
email: this.email
};
}
}
class UserValidator {
static validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
static validateUsername(username) {
return username && username.length >= 3;
}
}
class UserRepository {
async save(user) {
// 保存用户到数据库
}
async findByUsername(username) {
// 从数据库查找用户
}
}
class EmailService {
async sendWelcomeEmail(user) {
// 发送欢迎邮件
}
}
class Logger {
static log(level, message) {
console.log(`[${new Date()}] [${level}] ${message}`);
}
}
开闭原则
对扩展开放,对修改关闭:
javascript
// 基础支付处理器
class PaymentProcessor {
processPayment(amount, method) {
throw new Error('processPayment方法必须在子类中实现');
}
}
// 具体支付方式
class CreditCardProcessor extends PaymentProcessor {
processPayment(amount, cardInfo) {
console.log(`使用信用卡支付 ¥${amount}`);
// 信用卡支付逻辑
return { success: true, transactionId: 'cc_' + Date.now() };
}
}
class PayPalProcessor extends PaymentProcessor {
processPayment(amount, paypalInfo) {
console.log(`使用PayPal支付 ¥${amount}`);
// PayPal支付逻辑
return { success: true, transactionId: 'pp_' + Date.now() };
}
}
class AlipayProcessor extends PaymentProcessor {
processPayment(amount, alipayInfo) {
console.log(`使用支付宝支付 ¥${amount}`);
// 支付宝支付逻辑
return { success: true, transactionId: 'ap_' + Date.now() };
}
}
// 支付管理器
class PaymentManager {
constructor() {
this.processors = new Map();
}
registerProcessor(type, processor) {
this.processors.set(type, processor);
}
processPayment(type, amount, paymentInfo) {
const processor = this.processors.get(type);
if (!processor) {
throw new Error(`不支持的支付方式: ${type}`);
}
return processor.processPayment(amount, paymentInfo);
}
}
// 使用示例
const paymentManager = new PaymentManager();
paymentManager.registerProcessor('creditcard', new CreditCardProcessor());
paymentManager.registerProcessor('paypal', new PayPalProcessor());
paymentManager.registerProcessor('alipay', new AlipayProcessor());
// 添加新的支付方式不需要修改现有代码
class WeChatPayProcessor extends PaymentProcessor {
processPayment(amount, wechatInfo) {
console.log(`使用微信支付 ¥${amount}`);
return { success: true, transactionId: 'wx_' + Date.now() };
}
}
paymentManager.registerProcessor('wechat', new WeChatPayProcessor());
里氏替换原则
子类应该能够替换父类:
javascript
// 基类
class Bird {
fly() {
console.log('鸟儿在飞翔');
}
}
// 违反LSP的设计
class Penguin extends Bird {
fly() {
throw new Error('企鹅不会飞'); // 违反了LSP
}
}
// 更好的设计
class Animal {
move() {
console.log('动物在移动');
}
}
class FlyingBird extends Animal {
move() {
this.fly();
}
fly() {
console.log('鸟儿在飞翔');
}
}
class SwimmingBird extends Animal {
move() {
this.swim();
}
swim() {
console.log('鸟儿在游泳');
}
}
class Eagle extends FlyingBird {}
class Penguin extends SwimmingBird {}
function makeAnimalMove(animal) {
animal.move();
}
makeAnimalMove(new Eagle());
makeAnimalMove(new Penguin());
性能考量
避免在构造函数中创建方法
javascript
// 不好的做法
class BadCounter {
constructor() {
this.count = 0;
this.increment = function() {
this.count++;
};
}
}
// 好的做法
class GoodCounter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
合理使用私有字段
javascript
class OptimizedClass {
constructor(publicData, privateData) {
this.publicData = publicData;
this.#privateData = privateData;
}
#privateData;
getPrivateData() {
return this.#privateData;
}
}
测试友好的类设计
javascript
class UserService {
constructor(userRepository, emailService, logger) {
this.userRepository = userRepository;
this.emailService = emailService;
this.logger = logger;
}
async createUser(userData) {
try {
// 验证用户数据
this.validateUserData(userData);
// 创建用户
const user = await this.userRepository.create(userData);
// 发送欢迎邮件
await this.emailService.sendWelcomeEmail(user);
// 记录日志
this.logger.info(`用户创建成功: ${user.username}`);
return user;
} catch (error) {
this.logger.error(`用户创建失败: ${error.message}`);
throw error;
}
}
validateUserData(userData) {
if (!userData.username) {
throw new Error('用户名不能为空');
}
if (!userData.email) {
throw new Error('邮箱不能为空');
}
// 更多验证逻辑...
}
}
// 测试示例(使用Jest)
describe('UserService', () => {
let userService;
let mockUserRepository;
let mockEmailService;
let mockLogger;
beforeEach(() => {
mockUserRepository = {
create: jest.fn()
};
mockEmailService = {
sendWelcomeEmail: jest.fn()
};
mockLogger = {
info: jest.fn(),
error: jest.fn()
};
userService = new UserService(mockUserRepository, mockEmailService, mockLogger);
});
test('应该成功创建用户', async () => {
const userData = { username: 'test', email: 'test@example.com' };
const createdUser = { id: 1, ...userData };
mockUserRepository.create.mockResolvedValue(createdUser);
const result = await userService.createUser(userData);
expect(result).toEqual(createdUser);
expect(mockUserRepository.create).toHaveBeenCalledWith(userData);
expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledWith(createdUser);
expect(mockLogger.info).toHaveBeenCalled();
});
});
总结
JavaScript的类语法让面向对象编程变得更加直观。虽然底层仍然是基于原型的,但类提供了更清晰的代码组织方式。
核心概念包括封装、继承、多态和抽象。现代JavaScript支持私有字段、静态成员、访问器属性等特性,让类的功能更加完善。
在实际开发中,合理运用设计模式能够解决常见的编程问题。同时要注意遵循设计原则,编写可维护的代码。
类与函数式编程各有优势,选择合适的编程范式取决于具体的应用场景和团队偏好。
贡献者
huoshan