JavaScript中的面向对象编程
虽然JavaScript基于原型而非类,但它仍然支持面向对象编程的核心概念:封装、继承和多态。
从构造函数到ES6类
传统的构造函数模式
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `你好,我是${this.name},今年${this.age}岁。`;
};
Person.isAdult = function(age) {
return age >= 18;
};
const john = new Person("张三", 25);
console.log(john.greet()); // 你好,我是张三,今年25岁。
console.log(Person.isAdult(20)); // true
ES6类语法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `你好,我是${this.name},今年${this.age}岁。`;
}
static isAdult(age) {
return age >= 18;
}
}
const mary = new Person("李四", 30);
console.log(mary.greet()); // 你好,我是李四,今年30岁。
console.log(Person.isAdult(15)); // false
继承与多态
使用ES6实现继承
class Employee extends Person {
constructor(name, age, position, salary) {
super(name, age);
this.position = position;
this.salary = salary;
}
// 方法重写(多态)
greet() {
return `${super.greet()} 我的职位是${this.position}。`;
}
getAnnualSalary() {
return this.salary * 12;
}
}
const emp = new Employee("王五", 28, "前端工程师", 15000);
console.log(emp.greet()); // 你好,我是王五,今年28岁。 我的职位是前端工程师。
console.log(emp.getAnnualSalary()); // 180000
使用私有字段(ES2022)
class BankAccount {
#balance; // 私有字段
constructor(owner, initialBalance = 0) {
this.owner = owner;
this.#balance = initialBalance;
}
deposit(amount) {
if (amount <= 0) {
throw new Error("存款金额必须大于0");
}
this.#balance += amount;
return this.#balance;
}
withdraw(amount) {
if (amount this.#balance) {
throw new Error("余额不足");
}
this.#balance -= amount;
return this.#balance;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount("赵六", 1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // 错误:私有字段无法外部访问
JavaScript设计模式实战
1. 工厂模式
工厂模式提供了一种创建对象的接口,而无需指定具体类。
class Car {
constructor(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
getInfo() {
return `${this.year} ${this.make} ${this.model}`;
}
}
class Truck {
constructor(make, model, year, payload) {
this.make = make;
this.model = model;
this.year = year;
this.payload = payload;
}
getInfo() {
return `${this.year} ${this.make} ${this.model} (载荷: ${this.payload}吨)`;
}
}
class VehicleFactory {
static createVehicle(type, options) {
switch (type) {
case 'car':
return new Car(options.make, options.model, options.year);
case 'truck':
return new Truck(
options.make,
options.model,
options.year,
options.payload
);
default:
throw new Error(`未知的车辆类型: ${type}`);
}
}
}
// 使用工厂创建对象
const myCar = VehicleFactory.createVehicle('car', {
make: 'Toyota',
model: 'Camry',
year: 2022
});
const myTruck = VehicleFactory.createVehicle('truck', {
make: 'Ford',
model: 'F-150',
year: 2021,
payload: 2.5
});
console.log(myCar.getInfo()); // 2022 Toyota Camry
console.log(myTruck.getInfo()); // 2021 Ford F-150 (载荷: 2.5吨)
2. 单例模式
确保一个类只有一个实例,并提供全局访问点。
class AppConfig {
static instance = null;
constructor() {
if (AppConfig.instance) {
return AppConfig.instance;
}
this.settings = {
apiUrl: 'https://api.example.com',
timeout: 5000,
theme: 'dark'
};
AppConfig.instance = this;
}
static getInstance() {
if (!AppConfig.instance) {
AppConfig.instance = new AppConfig();
}
return AppConfig.instance;
}
getSetting(key) {
return this.settings[key];
}
setSetting(key, value) {
this.settings[key] = value;
}
}
// 使用单例
const config1 = AppConfig.getInstance();
const config2 = AppConfig.getInstance();
console.log(config1 === config2); // true
config1.setSetting('theme', 'light');
console.log(config2.getSetting('theme')); // light
3. 观察者模式
定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。
class EventObserver {
constructor() {
this.observers = [];
}
subscribe(fn) {
this.observers.push(fn);
return () => this.unsubscribe(fn); // 返回取消订阅的函数
}
unsubscribe(fn) {
this.observers = this.observers.filter(subscriber => subscriber !== fn);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
// 应用示例:购物车
class ShoppingCart {
constructor() {
this.items = [];
this.cartObserver = new EventObserver();
}
addItem(product, quantity = 1) {
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
this.cartObserver.notify(this.getCartSummary());
}
removeItem(productId) {
this.items = this.items.filter(item => item.product.id !== productId);
this.cartObserver.notify(this.getCartSummary());
}
getCartSummary() {
const total = this.items.reduce((sum, item) => {
return sum + (item.product.price * item.quantity);
}, 0);
const itemCount = this.items.reduce((count, item) => {
return count + item.quantity;
}, 0);
return {
items: this.items,
total,
itemCount
};
}
onCartChange(fn) {
return this.cartObserver.subscribe(fn);
}
}
// 使用观察者模式
const cart = new ShoppingCart();
// 订阅购物车变化
const unsubscribe = cart.onCartChange(cartSummary => {
console.log('购物车已更新:', cartSummary);
});
// 模拟产品
const products = [
{ id: 1, name: 'JavaScript高级程序设计', price: 99 },
{ id: 2, name: 'CSS权威指南', price: 79 },
{ id: 3, name: 'HTML5与CSS3基础教程', price: 69 }
];
// 添加商品
cart.addItem(products[0]);
cart.addItem(products[1], 2);
cart.addItem(products[0]); // 再次添加同一商品
// 移除商品
setTimeout(() => {
cart.removeItem(2);
}, 1000);
// 取消订阅
setTimeout(() => {
unsubscribe();
console.log('已取消订阅购物车通知');
}, 2000);
实战案例:构建一个简单的UI组件系统
使用面向对象和设计模式创建一个可扩展的UI组件系统。
// 基础组件类
class UIComponent {
constructor(element = null) {
this.element = element;
this.initialized = false;
}
init() {
if (this.initialized || !this.element) return;
this.bindEvents();
this.initialized = true;
console.log(`${this.constructor.name} 初始化完成`);
}
bindEvents() {
// 由子类实现具体的事件绑定
}
destroy() {
this.initialized = false;
console.log(`${this.constructor.name} 已销毁`);
}
}
// 模态框组件
class Modal extends UIComponent {
constructor(element) {
super(element);
this.closeButton = this.element.querySelector('.modal-close');
this.overlay = this.element.querySelector('.modal-overlay');
}
bindEvents() {
this.closeButton.addEventListener('click', () => this.hide());
this.overlay.addEventListener('click', () => this.hide());
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') this.hide();
});
}
show() {
this.element.style.display = 'block';
document.body.style.overflow = 'hidden';
}
hide() {
this.element.style.display = 'none';
document.body.style.overflow = '';
}
}
// 标签页组件
class Tabs extends UIComponent {
constructor(element) {
super(element);
this.tabButtons = this.element.querySelectorAll('.tab-button');
this.tabPanes = this.element.querySelectorAll('.tab-pane');
}
bindEvents() {
this.tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabId = button.getAttribute('data-tab');
this.switchTab(tabId);
});
});
}
switchTab(tabId) {
// 更新按钮状态
this.tabButtons.forEach(button => {
button.classList.toggle('active', button.getAttribute('data-tab') === tabId);
});
// 更新面板显示
this.tabPanes.forEach(pane => {
pane.classList.toggle('active', pane.getAttribute('data-tab') === tabId);
});
}
}
// 组件工厂
class ComponentFactory {
static components = {
modal: Modal,
tabs: Tabs
};
static createComponent(type, element) {
const ComponentClass = this.components[type];
if (!ComponentClass) {
throw new Error(`未知的组件类型: ${type}`);
}
const component = new ComponentClass(element);
component.init();
return component;
}
static registerComponent(type, ComponentClass) {
if (typeof ComponentClass !== 'function') {
throw new Error('组件必须是一个类');
}
this.components[type] = ComponentClass;
console.log(`已注册组件: ${type}`);
}
}
// 使用组件系统
document.addEventListener('DOMContentLoaded', () => {
// 初始化现有组件
const modals = document.querySelectorAll('[data-component="modal"]');
modals.forEach(modalElement => {
ComponentFactory.createComponent('modal', modalElement);
});
const tabs = document.querySelectorAll('[data-component="tabs"]');
tabs.forEach(tabElement => {
ComponentFactory.createComponent('tabs', tabElement);
});
// 注册新组件示例
class Accordion extends UIComponent {
constructor(element) {
super(element);
this.items = this.element.querySelectorAll('.accordion-item');
}
bindEvents() {
this.items.forEach(item => {
const header = item.querySelector('.accordion-header');
header.addEventListener('click', () => {
item.classList.toggle('active');
});
});
}
}
ComponentFactory.registerComponent('accordion', Accordion);
// 初始化手风琴组件
const accordions = document.querySelectorAll('[data-component="accordion"]');
accordions.forEach(accordionElement => {
ComponentFactory.createComponent('accordion', accordionElement);
});
});
最佳实践与注意事项
1. 组合优于继承
优先使用对象组合而不是类继承,提高代码的灵活性。
// 使用组合的例子
const canLog = {
log(message) {
console.log(`${this.name}: ${message}`);
}
};
const canValidate = {
validate() {
return this.value !== undefined && this.value !== null;
}
};
class DataField {
constructor(name, value) {
this.name = name;
this.value = value;
// 组合功能
Object.assign(this, canLog, canValidate);
}
}
const field = new DataField('username', 'john_doe');
field.log('字段已创建'); // username: 字段已创建
console.log(field.validate()); // true
2. 遵循SOLID原则
- 单一职责原则:一个类只应有一个引起变化的原因
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:子类应该能够替换父类
- 接口隔离原则:使用多个特定接口比使用单一通用接口更好
- 依赖倒置原则:依赖抽象而不是具体实现
3. 使用设计模式的时机
不要过度设计,只在真正需要时使用设计模式。简单的代码通常比过度设计的复杂代码更好。
总结
JavaScript的面向对象编程和设计模式提供了构建可维护、可扩展应用程序的强大工具:
- ES6类语法提供了更清晰的OOP实现方式
- 设计模式解决特定场景下的常见问题
- 组合模式提供了比继承更灵活的选择
- 遵循SOLID原则编写高质量的代码
通过掌握这些概念,你可以构建更健壮、更易维护的JavaScript应用程序。
// 可运行的示例代码
console.log(“JavaScript面向对象编程示例”);
// 简单的类示例
class DemoClass {
constructor(value) {
this.value = value;
}
getValue() {
return this.value;
}
}
const demo = new DemoClass(“示例值”);
console.log(demo.getValue());