0

0

JavaScript 设计模式指南

王林

王林

发布时间:2024-08-07 10:02:21

|

690人浏览过

|

来源于dev.to

转载

侯赛因·阿里夫撰写✏️

想象一下这样的情况:一群建筑师想要设计一座摩天大楼。在设计阶段,他们必须考虑很多因素,例如:

  • 建筑风格——建筑应该是野兽派、极简主义还是其他风格?
  • 底座的宽度——需要多大的尺寸才能防止大风天倒塌?
  • 预防自然灾害 - 根据该建筑物的位置需要采取哪些预防性结构措施来防止地震、洪水等造成的损坏?

需要考虑的因素有很多,但有一点是可以确定的:很可能已经有一份蓝图可以帮助建造这座摩天大楼。如果没有通用的设计或计划,这些架构师将不得不重新发明轮子,这可能会导致混乱和多重低效率。

类似地,在编程世界中,开发人员经常参考一组设计模式来帮助他们构建软件,同时遵循干净的代码原则。此外,这些模式无处不在,从而让程序员专注于交付新功能,而不是每次都重新发明轮子。

在本文中,您将了解一些常用的 javascript 设计模式,并且我们将一起构建小型 node.js 项目来说明每种设计模式的用法。

立即学习Java免费学习笔记(深入)”;

软件工程中的设计模式是什么?

设计模式是预先制作的蓝图,开发人员可以对其进行定制以解决编码期间的重复设计问题。要记住的一件重要的事情是,这些蓝图不是代码片段,而是应对即将到来的挑战的一般概念。

设计模式有很多好处:

  • 经过尝试和测试 - 它们解决了软件设计中的无数问题。了解并应用代码中的模式很有用,因为这样做可以帮助您使用面向对象设计的原则解决各种问题
  • 定义通用语言 — 设计模式帮助团队以有效的方式进行沟通。例如,队友可以说“我们应该使用工厂方法来解决这个问题”,每个人都会明白他的意思以及他们建议背后的动机

在本文中,我们将介绍三类设计模式:

  • 创建型 — 用于创建对象
  • 结构 - 组装这些对象以形成一个工作结构
  • 行为 - 在这些对象之间分配职责

让我们看看这些设计模式的实际应用!

创意设计模式

顾名思义,创建模式包含各种帮助开发人员创建对象的方法。

工厂

工厂方法是一种创建对象的模式,可以更好地控制对象的创建。这种方法适用于我们希望将对象创建逻辑集中在一处的情况。

以下是一些示例代码,展示了此模式的实际效果:

//file name: factory-pattern.js
//use the factory javascript design pattern:
//step 1: create an interface for our object. in this case, we want to create a car
const createcar = ({ company, model, size }) => ({
//the properties of the car:
  company,
  model,
  size,
  //a function that prints out the car's properties:
  showdescription() {
    console.log(
      "the all new ",
      model,
      " is built by ",
      company,
      " and has an engine capacity of ",
      size,
      " cc "
    );
  },
});
//use the 'createcar' interface to create a car
const challenger = createcar({
  company: "dodge",
  model: "challenger",
  size: 6162,
});
//print out this object's traits:
challenger.showdescription();

让我们逐段分解这段代码:createcarcar

  • 每辆汽车都有三个属性:公司、型号和尺寸。此外,我们还定义了一个 showdescription 函数,它将注销对象的属性。此外,请注意 createcar 方法演示了我们如何在内存中实例化对象时进行精细控制
  • 后来,我们使用 createcar 实例来初始化一个名为challenger 的对象
  • 最后,在最后一行,我们在挑战者实例上调用了 showdescription

我们来测试一下吧!我们应该期望程序注销我们新创建的 car 实例的详细信息:JavaScript 设计模式指南

建设者

builder 方法让我们可以使用逐步的对象构造来构建对象。因此,这种设计模式非常适合我们想要创建对象并仅应用必要功能的情况。因此,这提供了更大的灵活性。

这是使用构建器模式创建 car 对象的代码块:

//builder-pattern.js
//step 1: create a class reperesentation for our toy car:
class car {
  constructor({ model, company, size }) {
    this.model = model;
    this.company = company;
    this.size = size;
  }
}
//use the 'builder' pattern to extend this class and add functions
//note that we have seperated these functions in their entities.
//this means that we have not defined these functions in the 'car' definition.
car.prototype.showdescription = function () {
  console.log(
    this.model +
      " is made by " +
      this.company +
      " and has an engine capacity of " +
      this.size +
      " cc "
  );
};
car.prototype.reducesize = function () {
  const size = this.size - 2; //function to reduce the engine size of the car.
  this.size = size;
};
const challenger = new car({
  company: "dodge",
  model: "challenger",
  size: 6162,
});
//finally, print out the properties of the car before and after reducing the size:
challenger.showdescription();
console.log('reducing size...');
//reduce size of car twice:
challenger.reducesize();
challenger.reducesize();
challenger.showdescription();

这是我们在上面的代码块中所做的事情:

  • 作为第一步,我们创建了一个 car 类,它将帮助我们实例化对象。请注意,之前在工厂模式中,我们使用了 createcar 函数,但这里我们使用的是类。这是因为 javascript 中的类允许开发人员分段构造对象。或者,简单地说,为了实现 javascript 构建器设计模式,我们必须选择面向对象的范例
  • 之后,我们使用原型对象来扩展 car 类。在这里,我们创建了两个函数——showdescription和reducesize
  • 后来,我们创建了我们的 car 实例,将其命名为 challenger,然后注销了它的信息
  • 最后,我们调用这个对象的reducesize方法来减少它的大小,然后我们再次打印它的属性

预期输出应该是挑战者对象在我们将其大小减少四个单位之前和之后的属性: JavaScript 设计模式指南  这证实了我们在 javascript 中的构建器模式实现是成功的!

结构设计模式

结构设计模式专注于我们程序的不同组件如何协同工作。

适配器

适配器方法允许接口冲突的对象一起工作。这种模式的一个很好的用例是当我们想要在不引入重大更改的情况下使旧代码适应新代码库时:

//adapter-pattern.js
//create an array with two fields: 
//'name' of a band and the number of 'sold' albums
const groupswithsoldalbums = [
  {
    name: "twice",
    sold: 23,
  },
  { name: "blackpink", sold: 23 },
  { name: "aespa", sold: 40 },
  { name: "newjeans", sold: 45 },
];
console.log("before:");
console.log(groupswithsoldalbums);
//now we want to add this object to the 'groupswithsoldalbums' 
//problem: our array can't accept the 'revenue' field
// we want to change this field to 'sold'
var illit = { name: "illit", revenue: 300 };
//solution: create an 'adapter' to make both of these interfaces..
//..work with each other
const cost_per_album = 30;
const converttoalbumssold = (group) => {
  //make a copy of the object and change its properties
  const tempgroup = { name: group.name, sold: 0 };
  tempgroup.sold = parseint(group.revenue / cost_per_album);
  //return this copy:
  return tempgroup;
};
//use our adapter to make a compatible copy of the 'illit' object:
illit = converttoalbumssold(illit);
//now that our interfaces are compatible, we can add this object to the array
groupswithsoldalbums.push(illit);
console.log("after:");
console.log(groupswithsoldalbums);

这是此片段中发生的事情:

  • 首先,我们创建了一个名为 groupswithsoldalbums 的对象数组。每个对象都会有一个名称和出售的财产
  • 然后我们创建了一个 illit 对象,它有两个属性 - 名称和收入。在这里,我们想将其附加到 groupswithsoldalbums 数组中。这可能是一个问题,因为数组不接受收入属性
  • 要缓解这个问题,请使用适配器方法。 converttoalbumssold 函数将调整 illit 对象,以便将其添加到我们的数组中

运行此代码时,我们希望我们的 illit 对象成为 groupswithsoldalbums 列表的一部分:JavaScript 设计模式指南

装饰者

此设计模式允许您在创建后向对象添加新方法和属性。当我们想要在运行时扩展组件的功能时,这非常有用。

AIBox 一站式AI创作平台
AIBox 一站式AI创作平台

AIBox365一站式AI创作平台,支持ChatGPT、GPT4、Claue3、Gemini、Midjourney等国内外大模型

下载

如果您有 react 背景,这与使用高阶组件类似。下面是一段代码,演示了 javascript 装饰器设计模式的使用:

//file name: decorator-pattern.js
//step 1: create an interface
class musicartist {
  constructor({ name, members }) {
    this.name = name;
    this.members = members;
  }
  displaymembers() {
    console.log(
      "group name",
      this.name,
      " has",
      this.members.length,
      " members:"
    );
    this.members.map((item) => console.log(item));
  }
}
//step 2: create another interface that extends the functionality of musicartist
class performingartist extends musicartist {
  constructor({ name, members, eventname, songname }) {
    super({ name, members });
    this.eventname = eventname;
    this.songname = songname;
  }
  perform() {
    console.log(
      this.name +
        " is now performing at " +
        this.eventname +
        " they will play their hit song " +
        this.songname
    );
  }
}
//create an instance of performingartist and print out its properties:
const akmu = new performingartist({
  name: "akmu",
  members: ["suhyun", "chanhyuk"],
  eventname: "mnet",
  songname: "hero",
});
akmu.displaymembers();
akmu.perform();

让我们解释一下这里发生了什么:

  • 第一步,我们创建了一个 musicartist 类,它有两个属性:name 和 members。它还有一个displaymembers方法,会打印出当前乐队的名字和成员
  • 后来,我们扩展了 musicartist 并创建了一个名为 performingartist 的子类。除了 musicartist 的属性之外,新类还将有两个属性:eventname 和 songname。此外,performingartist还有一个perform函数,它将名称和songname属性打印到控制台
  • 之后,我们创建了一个 performingartist 实例并将其命名为 akmu
  • 最后,我们注销了 akmu 的详细信息并调用了执行函数

代码的输出应该确认我们通过 performingartist 类成功为乐队添加了新功能:JavaScript 设计模式指南

行为设计模式

此类别重点关注程序中的不同组件如何相互通信。

责任链

责任链设计模式允许通过组件链传递请求。当程序收到请求时,链中的组件要么处理它,要么将其传递,直到程序找到合适的处理程序。

这是解释此设计模式的插图: JavaScript 设计模式指南 存储桶或请求沿着组件链向下传递,直到找到有能力的组件。当找到合适的组件时,它将处理该请求。来源:refactoring guru。[/caption] 此模式的最佳用途是 express 中间件函数链,其中函数可以处理传入请求或通过 next() 方法将其传递给下一个函数:

//real-world situation: event management of a concert
//implement cor javascript design pattern:
//step 1: create a class that will process a request
class leader {
  constructor(responsibility, name) {
    this.responsibility = responsibility;
    this.name = name;
  }
  //the 'setnext' function will pass the request to the next component in the chain.
  setnext(handler) {
    this.nexthandler = handler;
    return handler;
  }
  handle(responsibility) {
  //switch to the next handler and throw an error message:
    if (this.nexthandler) {
      console.log(this.name + " cannot handle operation: " + responsibility);
      return this.nexthandler.handle(responsibility);
    }
    return false;
  }
}
//create two components to handle certain requests of a concert
//first component: handle the lighting of the concert:
class lightsengineerlead extends leader {
  constructor(name) {
    super("light management", name);
  }
  handle(responsibility) {
  //if 'lightsengineerlead' gets the responsibility(request) to handle lights,
  //then they will handle it
    if (responsibility == "lights") {
      console.log("the lights are now being handled by ", this.name);
      return;
    }
    //otherwise, pass it to the next component.
    return super.handle(responsibility);
  }
}

//second component: handle the sound management of the event:
class soundengineerlead extends leader {
  constructor(name) {
    super("sound management", name);
  }
  handle(responsibility) {
  //if 'soundengineerlead' gets the responsibility to handle sounds,
  // they will handle it
    if (responsibility == "sound") {
      console.log("the sound stage is now being handled by ", this.name);
      return;
    }
    //otherwise, forward this request down the chain:
    return super.handle(responsibility);
  }
}
//create two instances to handle the lighting and sounds of an event:
const minji = new lightsengineerlead("minji");
const danielle = new soundengineerlead("danielle");
//set 'danielle' to be the next handler component in the chain.
minji.setnext(danielle);
//ask minji to handle the sound and lights:
//since minji can't handle sound management, 
// we expect this request to be forwarded 
minji.handle("sound");
//minji can handle lights, so we expect it to be processed
minji.handle("lights");

在上面的代码中,我们模拟了音乐会上的情况。在这里,我们希望不同的人承担不同的责任。如果一个人无法处理某项任务,则会将其委托给列表中的下一个人。

最初,我们声明了一个具有两个属性的 leader 基类:

  • 责任——领导者能够处理的任务类型
  • name — 处理程序的名称

此外,每个leader都会有两个功能:

  • setnext:顾名思义,这个函数会在责任链上添加一个leader
  • handle:该函数会检查当前leader是否可以处理某个职责;否则,它将通过 setnext 方法将该责任转发给下一个人

接下来,我们创建了两个子类,分别称为 lightsengineerlead(负责照明)和 soundengineerlead(处理音频)。后来,我们初始化了两个对象——minji和danielle。我们使用 setnext 函数将 danielle 设置为责任链中的下一个处理程序。

最后,我们请minji处理声音和灯光。

当代码运行时,我们期望 minji 尝试处理我们的声音和灯光职责。由于minji不是音频工程师,因此应该将sound交给有能力的处理人员。在本例中,是丹尼尔:JavaScript 设计模式指南

战略

策略方法允许您定义算法集合并在运行时在它们之间进行交换。此模式对于导航应用程序很有用。这些应用程序可以利用此模式在不同用户类型(骑行、驾驶或跑步)之间切换路线:

此代码块演示了 javascript 代码中的策略设计模式:

//situation: Build a calculator app that executes an operation between 2 numbers.
//depending on the user input, change between division and modulus operations

class CalculationStrategy {
  performExecution(a, b) {}
}
//create an algorithm for division
class DivisionStrategy extends CalculationStrategy {
  performExecution(a, b) {
    return a / b;
  }
}
//create another algorithm for performing modulus
class ModuloStrategy extends CalculationStrategy {
  performExecution(a, b) {
    return a % b;
  }
}
//this class will help the program switch between our algorithms:
class StrategyManager {
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  executeStrategy(a, b) {
    return this.strategy.performExecution(a, b);
  }
}

const moduloOperation = new ModuloStrategy();
const divisionOp = new DivisionStrategy();
const strategyManager = new StrategyManager();
//use the division algorithm to divide two numbers:
strategyManager.setStrategy(divisionOp);
var result = strategyManager.executeStrategy(20, 4);
console.log("Result is: ", result);
//switch to the modulus strategy to perform modulus:
strategyManager.setStrategy(moduloOperation);
result = strategyManager.executeStrategy(20, 4);
console.log("Result of modulo is ", result);

这是我们在上面的块中所做的:

  • 首先,我们创建了一个基本的 calculationstrategy 抽象类,它将处理两个数字 — a 和 b
  • 然后我们定义了两个子类——divisionstrategy 和 modulostrategy。这两个类由除法和求模算法组成并返回输出
  • 接下来,我们声明了一个 strategymanager 类,它将让程序在不同的算法之间切换
  • 最后,我们使用 divisionstrategy 和 modulostrategy 算法来处理两个数字并返回其输出。为了在这些策略之间切换,使用了strategymanager实例

当我们执行这个程序时,预期的输出是strategymanager首先使用divisionstrategy来除两个数字,然后切换到modulostrategy以返回这些输入的模数:JavaScript 设计模式指南

结论

在本文中,我们了解了设计模式是什么,以及它们为什么在软件开发行业中有用。此外,我们还了解了不同类别的 javascript 设计模式并在代码中实现了它们。


logrocket:通过了解上下文更轻松地调试 javascript 错误

调试代码始终是一项繁琐的任务。但你越了解自己的错误,就越容易纠正它们。

logrocket 允许您以新的、独特的方式理解这些错误。我们的前端监控解决方案跟踪用户与 javascript 前端的互动,使您能够准确查看用户的操作导致了错误。

JavaScript 设计模式指南

logrocket 记录控制台日志、页面加载时间、堆栈跟踪、带有标头 + 正文的慢速网络请求/响应、浏览器元数据和自定义日志。了解 javascript 代码的影响从未如此简单!

免费试用。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

183

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

226

2025.12.18

Node.js后端开发与Express框架实践
Node.js后端开发与Express框架实践

本专题针对初中级 Node.js 开发者,系统讲解如何使用 Express 框架搭建高性能后端服务。内容包括路由设计、中间件开发、数据库集成、API 安全与异常处理,以及 RESTful API 的设计与优化。通过实际项目演示,帮助开发者快速掌握 Node.js 后端开发流程。

425

2026.02.10

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1958

2023.10.19

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号