JavaScript设计模式「基于ES2024」:其他模式-模块模式

94 阅读2分钟

模块模式提供了一种方法来封装相关的方法和变量到一个独立的单元中,是JavaScript中一种非常常用的设计模式。这种模式在ES6引入模块系统之前十分重要,不过哪怕是现代JavaScript,在遇到需要创建私有状态和公共接口的场景中,它仍然有其用处。

PS:...多少人不知道ES6已经是9年前的产物了,你的公司用上了吗?

以下通过IIFE(立即调用函数表达式)和现代类语法实现的模块模式进行演示:

// 使用 IIFE (立即调用函数表达式) 创建模块
const ShoppingCart = (function() {
    // 私有变量和方法
    let items = [];
    
    function calculateTotal() {
        return items.reduce((total, item) => total + item.price * item.quantity, 0);
    }

    // 返回公共接口
    return {
        addItem(name, price, quantity = 1) {
            items.push({ name, price, quantity });
            console.log(`Added ${quantity} ${name}(s) to the cart.`);
        },

        removeItem(name) {
            const index = items.findIndex(item => item.name === name);
            if (index !== -1) {
                items.splice(index, 1);
                console.log(`Removed ${name} from the cart.`);
            } else {
                console.log(`${name} not found in the cart.`);
            }
        },

        getItemCount() {
            return items.reduce((count, item) => count + item.quantity, 0);
        },

        getTotalPrice() {
            return calculateTotal().toFixed(2);
        },

        clearCart() {
            items = [];
            console.log("Cart cleared.");
        },

        listItems() {
            console.log("Cart contents:");
            items.forEach(item => {
                console.log(`${item.name} - Quantity: ${item.quantity}, Price: $${item.price.toFixed(2)}`);
            });
        }
    };
})();

// 使用ES2024类语法的替代实现
class ModernShoppingCart {
    #items = [];

    addItem(name, price, quantity = 1) {
        this.#items.push({ name, price, quantity });
        console.log(`Added ${quantity} ${name}(s) to the cart.`);
    }

    removeItem(name) {
        const index = this.#items.findIndex(item => item.name === name);
        if (index !== -1) {
            this.#items.splice(index, 1);
            console.log(`Removed ${name} from the cart.`);
        } else {
            console.log(`${name} not found in the cart.`);
        }
    }

    getItemCount() {
        return this.#items.reduce((count, item) => count + item.quantity, 0);
    }

    getTotalPrice() {
        return this.#calculateTotal().toFixed(2);
    }

    clearCart() {
        this.#items = [];
        console.log("Cart cleared.");
    }

    listItems() {
        console.log("Cart contents:");
        this.#items.forEach(item => {
            console.log(`${item.name} - Quantity: ${item.quantity}, Price: $${item.price.toFixed(2)}`);
        });
    }

    #calculateTotal() {
        return this.#items.reduce((total, item) => total + item.price * item.quantity, 0);
    }
}

// 使用示例
function demonstrateModulePattern() {
    console.log("Using the IIFE Module Pattern:");
    ShoppingCart.addItem("Apple", 0.5, 3);
    ShoppingCart.addItem("Banana", 0.3, 2);
    ShoppingCart.listItems();
    console.log(`Total items: ${ShoppingCart.getItemCount()}`);
    console.log(`Total price: $${ShoppingCart.getTotalPrice()}`);
    ShoppingCart.removeItem("Apple");
    ShoppingCart.listItems();
    ShoppingCart.clearCart();

    console.log("\nUsing the Modern Class-based Pattern:");
    const cart = new ModernShoppingCart();
    cart.addItem("Orange", 0.75, 4);
    cart.addItem("Grape", 2.5);
    cart.listItems();
    console.log(`Total items: ${cart.getItemCount()}`);
    console.log(`Total price: $${cart.getTotalPrice()}`);
    cart.removeItem("Orange");
    cart.listItems();
    cart.clearCart();
}

demonstrateModulePattern();

实现思路

  • IIFE 模块模式

    • 使用立即调用函数表达式创建一个闭包。
    • 私有变量和函数在闭包内部定义。
    • 返回一个包含公共方法的对象,形成模块的公共接口。
  • 现代类实现(ModernShoppingCart

    • 使用类语法。
    • 利用私有字段(#items)和私有方法(#calculateTotal)来封装内部状态和逻辑。
    • 公共方法作为类的实例方法。

优点

  • 封装:隐藏了内部实现细节,只暴露必要的公共接口。
  • 命名空间:减少了全局命名空间的污染。
  • 可重用性:模块可以很容易地在不同的项目中重用。
  • 可维护性:将相关功能组织在一起,使代码更易于维护。