JavaScript 的 7 种设计模式 javascript设计模式

Javascript设计模式(JavaScript的7种设计模式)
当开始一个新项目时 , 我们不应该马上开始编程 。相反 , 我们应该首先定义项目的目的和范围 , 然后列出其功能或规格 。如果您已经开始编程或者正在从事一个复杂的项目 , 那么您应该选择一个最适合您的项目的设计模式 。
什么是设计模式?在软件工程中 , 设计模式是软件设计中常见问题的可重用解决方案 。设计模式也是有经验的开发人员针对特定问题的最佳实践 。可以作为编程的模板 。
为什么要使用设计模式?许多工程师要么认为设计模式浪费时间 , 要么不知道如何正确使用它们 。但是如果你能正确的使用设计模式 , 它会帮助你写出更好更可读的代码 , 代码也会更容易维护和理解 。
最重要的是 , 设计模式为软件开发人员提供了一个通用的词汇表 。它们可以让学习你的代码的人快速理解代码的意图 。例如 , 如果在您的项目中使用了decorator模式 , 那么新的开发人员可以很快知道这段代码的功能 , 这样他们就可以更专注于解决业务问题 , 而不是试图理解这段代码在做什么 。
现在我们知道了什么是设计模式以及它们的重要性 , 让我们深入了解一下JavaScript中的七种设计模式 。
一、模块模式一个模块是一个独立的代码 , 所以我们可以在不影响代码其余部分的情况下更新模块 。该模块还允许我们通过为变量创建单独的作用域来避免命名空污染 。当它们与其他代码解耦时 , 我们可以在其他项目中重用模块 。
模块是任何现代JavaScript应用程序不可或缺的一部分 , 它有助于保持代码的整洁、独立和有组织 。在JavaScript中创建模块的方法有很多种 , 其中一种就是模块模式 。
与其他编程语言不同 , JavaScript没有访问修饰符 , 也就是说 , 不能将变量声明为私有或公共的 。因此 , 模块模式也可以用来模拟包装的概念 。
该模块使用IIFE(立即调用函数表达式)、闭包和函数作用域来模拟封装的概念 。例如:
const myModule = (function() {const privateVariable = 'Hello World';function privateMethod() {console.log(privateVariable);}return {publicMethod: function() {privateMethod();}}})();myModule.publicMethod();
因为是IIFE , 所以代码会立即执行 , 返回的对象会赋给myModule变量 。因为闭包 , 即使在life完成之后 , 返回的对象仍然可以访问life内部定义的函数和变量 。
因此 , IIFE内部定义的变量和函数对外部是不可见的 , 从而使它们成为myModule模块的私有成员 。
执行代码后 , myModule变量如下所示:
const myModule = {publicMethod: function() {privateMethod();}};
因此 , 当我们调用publicMethod()时 , 它会调用privateMethod()例如:
// Prints 'Hello World'module.publicMethod();
二、揭示模块模式揭示模块模式是对Christian Heilmann的模块模式的一点改进 。模式的问题是我们必须创建新的公共函数来调用私有函数和变量 。
在这种模式下 , 我们将返回对象的属性映射到要公开的私有函数 。这就是为什么它被称为揭示模块模式 。例如:
const myRevealingModule = (function() {let privateVar = 'Peter';const publicVar= 'Hello World';function privateFunction() {console.log('Name: '+ privateVar);}function publicSetName(name) {privateVar = name;}function publicGetName() {privateFunction();}/** reveal methods and variables by assigning them to objectproperties */return {setName: publicSetName,greeting: publicVar,getName: publicGetName};})();myRevealingModule.setName('Mark');// prints Name: MarkmyRevealingModule.getName();
这种模式让我们更容易知道哪些函数和变量是公共的 , 这无形中提高了代码的可读性 。代码执行后 , myRevealingModule如下所示:
const myRevealingModule = {setName: publicSetName,greeting: publicVar,getName: publicGetName};
当我们调用myreealingmodule.setname ('mark ')时 , 我们实际上调用的是内部publicSetName 。当调用myRevealingModule.getName()时 , 实际调用的是内部publicGetName 。例如:
myRevealingModule.setName('Mark');// prints Name: MarkmyRevealingModule.getName();
与模块模式相比 , 模块模式的优势如下:
通过修改return语句中的一行 , 我们可以将成员从public改为private , 反之亦然 。
返回的对象不包含任何函数定义 , 所有的右表达式都是在IIFE中定义的 , 这样使得代码清晰可读 。
三 。ES6模块在ES6之前 , JavaScript没有内置模块 , 所以开发者必须依赖第三方库或模块模式来实现模块 。但是从ES6开始 , JavaScript有了内置模块 。
ES6的模块存储为文件 。每个文件只能有一个模块 。默认情况下 , 模块中的所有内容都是私有的 。使用export关键字公开函数、变量和类 。模块中的代码总是以严格模式运行 。
3.1导出模块有两种方法可以导出函数和变量声明:
在函数和变量声明前添加export关键字 。例如:
// utils.jsexport const greeting = 'Hello World';export function sum(num1, num2) {console.log('Sum:', num1, num2);return num1 + num2;}export function subtract(num1, num2) {console.log('Subtract:', num1, num2);return num1 - num2;}// This is a private functionfunction privateLog() {console.log('Private Function');}
在代码末尾添加export关键字 , 以公开函数和变量 。例如:
// utils.jsfunction multiply(num1, num2) {console.log('Multiply:', num1, num2);return num1 * num2;}function divide(num1, num2) {console.log('Divide:', num1, num2);return num1 / num2;}// This is a private functionfunction privateLog() {console.log('Private Function');}export {multiply, divide};
3.2导入模块与导出模块类似 , 有两种方法可以使用import关键字导入模块 。例如:
一次导入多个项目 。
// main.js// importing multiple itemsimport { sum, multiply } from './utils.js';console.log(sum(3, 7));console.log(multiply(3, 7));
导入所有模块
// main.js// importing all of moduleimport * as utils from './utils.js';console.log(utils.sum(3, 7));console.log(utils.multiply(3, 7));
3.3在导入和导出中使用别名重命名导出
// utils.jsfunction sum(num1, num2) {console.log('Sum:', num1, num2);return num1 + num2;}function multiply(num1, num2) {console.log('Multiply:', num1, num2);return num1 * num2;}export {sum as add, multiply};
重命名导入
// main.jsimport { add, multiply as mult } from './utils.js';console.log(add(3, 7));console.log(mult(3, 7));
四 。单一模式单一对象是只能实例化一次的对象 。如果它不存在 , singleton模式将创建该类的一个新实例 。如果有实例 , 则只返回对该对象的引用 。重复调用构造函数总是会得到相同的对象 。
JavaScript是一种内置单例的语言 。我们只是不称之为单例 , 我们称之为对象文字 。例如:
const user = {name: 'Peter',age: 25,job: 'Teacher',greet: function() {console.log('Hello!');}};
因为JavaScript中的每个对象都占用一个唯一的内存位置 , 而当我们调用这个用户对象时 , 实际上是在返回这个对象的引用 。
如果我们试图将用户变量复制到另一个变量并修改它 。例如:
const user1 = user;user1.name = 'Mark';
我们将看到两个对象都被修改了 , 因为JavaScript中的对象是通过引用而不是通过值来传递的 。因此 , 内存中只有一个对象 。例如:
// prints 'Mark'console.log(user.name);// prints 'Mark'console.log(user1.name);// prints trueconsole.log(user === user1);
您可以使用构造函数来实现单例模式 。例如:
let instance = null;function User() {if(instance) {return instance;}instance = this;this.name = 'Peter';this.age = 25;return instance;}const user1 = new User();const user2 = new User();// prints trueconsole.log(user1 === user2);
当调用这个构造函数时 , 它将检查实例对象是否存在 。如果对象不存在 , 则将此变量赋给实例变量 。如果对象存在 , 则只返回对象 。
单实例也可以用模块模式实现 。例如:
const singleton = (function() {let instance;function init() {return {name: 'Peter',age: 24,};}return {getInstance: function() {if(!instance) {instance = init();}return instance;}}})();const instanceA = singleton.getInstance();const instanceB = singleton.getInstance();// prints trueconsole.log(instanceA === instanceB);
在上面的代码中 , 我们通过调用singleton.getInstance方法创建了一个新实例 。如果实例已经存在 , 此方法只返回实例 。如果实例不存在 , 通过调用init()函数创建一个新的实例 。
动词 (verb的缩写)工厂模式工厂模式使用工厂方法创建对象 , 而不指定特定的类或构造器模式 。
【JavaScript 的 7 种设计模式 javascript设计模式】模式用于创建对象 , 而不暴露实例化的逻辑 。当我们需要根据具体情况生成不同的对象时 , 可以使用这种模式 。例如:
class Car{constructor(options) {this.doors = options.doors || 4;this.state = options.state || 'brand new';this.color = options.color || 'white';}}class Truck {constructor(options) {this.doors = options.doors || 4;this.state = options.state || 'used';this.color = options.color || 'black';}}class VehicleFactory {createVehicle(options) {if(options.vehicleType === 'car') {return new Car(options);} else if(options.vehicleType === 'truck') {return new Truck(options);}}}
这里 , 创建了一个car和一个truck类(带有一些默认值),用于创建新的Car和Truck对象 。此外 , VehicleFactory类被定义为根据options对象中的Vehiclefactory属性创建并返回一个新对象 。
const factory = new VehicleFactory();const car = factory.createVehicle({vehicleType: 'car',doors: 4,color: 'silver',state: 'Brand New'});const truck= factory.createVehicle({vehicleType: 'truck',doors: 2,color: 'white',state: 'used'});// Prints Car {doors: 4, state: "Brand New", color: "silver"}console.log(car);// Prints Truck {doors: 2, state: "used", color: "white"}console.log(truck);
我为类VehicleFactory创建了一个新的工厂对象 。然后 , 我们通过调用factory.createVehicle方法并传递options对象来创建一个新的car或truck对象 , 其vehicleType属性可能是Car或Truck 。
不及物动词装饰模式装饰模式用于在不修改现有类或构造函数的情况下扩展对象的功能 。这种模式可用于向对象添加功能 , 而无需修改底层代码 。
这种模式的一个简单例子是:
function Car(name) {this.name = name;// Default valuesthis.color = 'White';}// Creating a new Object to decorateconst tesla= new Car('Tesla Model 3');// Decorating the object with new functionalitytesla.setColor = function(color) {this.color = color;}tesla.setPrice = function(price) {this.price = price;}tesla.setColor('black');tesla.setPrice(49000);// prints blackconsole.log(tesla.color);
这种模式的一个更实际的例子是:
假设一辆车的成本取决于它的功能数量 。如果没有装饰模式 , 我们将不得不为不同的功能组合创建不同的类 , 并且每个类都有一个成本方法来计算成本 。例如:
class Car() {}class CarWithAC() {}class CarWithAutoTransmission {}class CarWithPowerLocks {}class CarWithACandPowerLocks {}
但是 , 通过decorator模式 , 我们可以创建一个基类car , 并通过decorator函数为不同的对象添加相应的成本逻辑 。
class Car {constructor() {// Default Costthis.cost = function() {return 20000;}}}// Decorator functionfunction carWithAC(car) {car.hasAC = true;const prevCost = car.cost();car.cost = function() {return prevCost + 500;}}// Decorator functionfunction carWithAutoTransmission(car) {car.hasAutoTransmission = true;const prevCost = car.cost();car.cost = function() {return prevCost + 2000;}}// Decorator functionfunction carWithPowerLocks(car) {car.hasPowerLocks = true;const prevCost = car.cost();car.cost = function() {return prevCost + 500;}}
首先 , 我们创建了汽车的基础类Car 。然后 , 为要添加的特性创建一个装饰器 , 这个装饰器将一个汽车对象作为参数 。然后 , 通过返回更新的汽车成本来覆盖对象的成本函数 , 并添加一个属性来标识是否添加了一个特性 。
要添加新功能 , 我们只需像下面这样做:
const car = new Car();console.log(car.cost());carWithAC(car);carWithAutoTransmission(car);carWithPowerLocks(car);
最后 , 我们可以这样计算汽车的成本:
// Calculating total cost of the carconsole.log(car.cost());
结论我们已经了解了JavaScript中使用的各种设计模式 , 但是这里没有可以用JavaScript实现的设计模式 。
虽然了解各种设计模式很重要 , 但不过度使用它们也同样重要 。在使用设计模式之前 , 你应该仔细考虑你的问题是否适合设计模式 。要知道某个模式是否适合你的问题 , 你要研究设计模式及其应用 。

    推荐阅读