JavaScript 与绅士风度:为什么你需要设计模式(以及它们如何简化你的生活)
\n\n你是否曾经打开过自己的旧 JavaScript 项目,心里想着:“这是谁写的?这简直就是意大利面条式代码!”?如果是的话——欢迎加入俱乐部。每个开发者迟早都会遇到代码变得混乱不堪的情况。这时,JavaScript 中的设计模式——针对典型问题的经过验证的解决方案——就会派上用场。
\n\n但让我们先达成共识:设计模式不是银弹。它们不是能在一夜之间让你的代码变得完美的魔法。它们是工具。而任何工具只有在你了解何时以及为何使用它时才是好的。这正是我们要讨论的内容。
\n\n用简单的话来说,什么是设计模式?
\n\n想象一下你是一名建筑师。你不会每次都重新发明轮子或门洞的形状。你使用经过验证的解决方案:拱门、柱子、承重墙。JS 中的设计模式就像是代码的“建筑蓝图”。
\n\n它们解决三个主要问题:
\n- \n
- 可复用性——无需重复造轮子; \n
- 可读性——其他开发者能立即理解你的逻辑; \n
- 可扩展性——代码易于扩展,而不会破坏一切。 \n
在本文中,我们将介绍四种最流行的、在日常工作中真正有用的设计模式。没有废话——只有代码和实践。
\n\n1. Singleton(单例模式):当你只需要一个实例时
\n\n有些情况下,应用程序中应该只有一个类的实例。例如:
\n- \n
- 全局状态存储(如 Redux store); \n
- 用于记录错误的日志记录器; \n
- 数据库或 API 连接。 \n
Singleton 模式确保无论你尝试创建对象多少次,你得到的都是同一个实例。
\n\n示例:访问计数器
\n\nclass Counter {\n constructor() {\n if (Counter.instance) {\n return Counter.instance;\n }\n this.count = 0;\n Counter.instance = this;\n }\n\n increment() {\n this.count++;\n }\n\n getCount() {\n return this.count;\n }\n}\n\n// 使用\nconst counter1 = new Counter();\nconst counter2 = new Counter();\n\ncounter1.increment();\ncounter1.increment();\n\nconsole.log(counter2.getCount()); // 2 — 因为它们是同一个对象\nconsole.log(counter1 === counter2); // true\n\n\n何时使用:当你必须拥有对资源的单一访问点时。但要小心——在 JS 中 Singleton 的一个常见副作用是全局状态,这很难测试。不要过度使用。
\n\n2. Module(模块模式):巧妙的封装
\n\n在 ES6 Modules 出现之前,开发者使用Module 模式来创建私有和公共方法。即使现在有了 import/export,这种方法对于组织单个文件内的逻辑仍然很有用。
\n\n秘诀在于闭包。我们创建一个函数,该函数返回一个包含方法的对象,而内部变量则从外部无法访问。
\n\n示例:在线商店购物车
\n\nconst ShoppingCart = (function() {\n // 私有变量\n let items = [];\n \n // 私有方法\n function calculateTotal() {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }\n\n // 公共 API\n return {\n addItem(product) {\n const existing = items.find(item => item.id === product.id);\n if (existing) {\n existing.quantity++;\n } else {\n items.push({ ...product, quantity: 1 });\n }\n },\n removeItem(productId) {\n items = items.filter(item => item.id !== productId);\n },\n getTotal() {\n return calculateTotal();\n },\n clear() {\n items = [];\n }\n };\n}