原文地址:[Learning JavaScript Design Patterns]
前言
编写可维护代码很重要!好的设计模式可以帮助我们注意到代码中重复出现的主题并对其进行优化(更好地编写可维护代码)。
什么是模式?
设计模式是一种可重用的解决方案。在编写应用时,可以在不同的情况下使用不同的设计模式来解决问题。
特性:
经过验证
易于重用
具有解释性:用集合或者简单的词汇来描述复杂的解决方案
模式并不能解决所有的设计问题,但能提供很多优势:
有助于发现可能导致开发过程中出现重大问题的小问题
设计模式是通用的,不同的编程语言都可以应用来改进代码结构
方便开发人员交流
基于设计模式的解决方案比临时解决方案更稳定强大
测试、原型模式、三法则
测试:完整的模式是经过严格审查和测试的
原型模式:尚未通过测试的模式,可能来自个人、社区
一个好的模式需要:
解决一个特定的问题
问题的解决方案不是显而易见的(好的设计模式通常间接地为问题提供解决方案)[?]
所描述的概念必须是已经被证明过的(设计模式需要证明它们的功能与描述的一致)
必须描述一种关系(设计模式需要描述深层次的系统结构和机制与代码的关系)
三法则:
模型如何被认为是成功的?(how/适合解决某种问题)
为什么模型被认为是成功的?(why/对于目标是起作用的)
具有普遍的/广泛的适用性
设计模式的结构
一个设计模式应该包括(设计自己的模式时):
名称和描述
上下文大纲
问题陈述
解决方案
设计:包括与用户的交互行为
执行(操作)指南
可视化表示
示例/举例
其他支持:包括其他模式
与其他模式的关系描述(借鉴/类似)
实际应用
讨论
编写设计模式(pass)
现代JavaScript语法和特征
解耦应用程序的重要性:模块化可以使程序更容易维护
-
import、export:模块化鼓励代码重用
.mjs is an extension used for JavaScript modules that helps us distinguish between module files and classic scripts (.js).
远程加载模块
-
动态导入:静态导入需要先下载、执行模块图后再运行代码,这可能会导致初始页面加载时间过长;按需加载可以提高初始加载时的性能
form.addEventListener("submit", e => { e.preventDefault(); import("/modules/cakeFactory.js") .then((module) => { // Do something with the module. module.oven.makeCupcake("sprinkles"); module.oven.makeMuffin("large"); }); }); let module = await import("/modules/cakeFactory.js"); 服务器模块:node 15.3.0开始支持js-modules(npm)
-
Classes With <u>Constructors, Getters & Setters</u>:
class Cake{ // We can define the body of a class" constructor // function by using the keyword "constructor" // with a list of class variables. constructor( name, toppings, price, cakeSize ){ this.name = name; this.cakeSize = cakeSize; this.toppings = toppings; this.price = price; } // As a part of ES2015+ efforts to decrease the unnecessary // use of "function" for everything, you'll notice that it's // dropped for cases such as the following. Here an identifier // followed by an argument list and a body defines a new method addTopping( topping ){ this.toppings.push( topping ); } // Getters can be defined by declaring get before // an identifier/method name and a curly body. get allToppings(){ return this.toppings; } get qualifiesForDiscount(){ return this.price > 5; } // Similar to getters, setters can be defined by using // the "set" keyword before an identifier set cakeSize( size ){ if ( size < 0){ throw "Cake must be a valid size - either small, medium or large"; } this._cakeSize = size;//写成“cakeSize”会报错“ Maximum call stack size exceeded” } } // Usage let cake = new Cake( "chocolate", ["chocolate chips"], 5, "large" ); // 继承 class BirthdayCake extends Cake { surprise() { console.log(`Happy Birthday!`); } } let birthdayCake = new BirthdayCake( "chocolate", ["chocolate chips"], 5, "large" ); birthdayCake.surprise();class Cookies { constructor(flavor) { this.flavor = flavor; } showTitle() { console.log(`The flavor of this cookie is ${this.flavor}.`); } } class FavoriteCookie extends Cookies { showTitle() { // super 调用父类的构造函数 super.showTitle(); console.log(`${this.flavor} is amazing.`); } } let myCookie = new FavoriteCookie('chocolate'); myCookie.showTitle();class CookieWithPrivateField { // 创建私有类字段 #privateField; } class CookieWithPrivateMethod { #privateMethod() { return 'delicious cookies'; } }
Anti-Patterns
描述对导致不良情况发生的特定问题的<u>不良解决方案</u>
描述<u>如何摆脱上述情况</u>以及如何从那里找到一个好的解决方案
每个设计都始于努力去解决问题和其解决方案之间的适应性。在程序投入生产并准备进入维护模式之后,创建anti-patterns可以避免发生常见的错误,重构代码时可以使解决方案的整体质量立即得到提高。
总而言之,反模式是一个值得记录的糟糕设计。 JavaScript 中的反模式示例如下:
通过在全局上下文中定义大量变量来污染全局命名空间
将字符串而不是函数传递给 setTimeout 或 setInterval,因为这会在内部触发 eval() 的使用
修改 Object 类原型
在内联表单中使用 JavaScript
严重滥用document.write,像 document.createElement 这样的原生 DOM 替代品更合适