JavaScript面向对象编程与设计模式实战 | 构建可维护大型应用

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());

JavaScript面向对象编程与设计模式实战 | 构建可维护大型应用
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 javascript JavaScript面向对象编程与设计模式实战 | 构建可维护大型应用 https://www.taomawang.com/web/javascript/1044.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务