写点什么

开发人员都应该了解的7种JavaScript设计模式

2020 年 6 月 30 日

开发人员都应该了解的7种JavaScript设计模式

本文最初发布于 codesource.io 网站,经原作者授权由 InfoQ 中文站翻译并分享。


开发人员将 JavaScript 设计模式作为解决问题的模板是很合适的,但并不是说这些模式可以代替开发人员的工作。


通过设计模式,我们可以将许多开发人员的经验结合起来,以优化过的方式来构造代码,从而解决我们所面对的问题。设计模式还提供了用于描述问题解决方案的通用词汇表,而不是去枯燥地描述代码的语法和语义。


JavaScript 设计模式可帮助开发人员编写出井井有条、美观且结构合理的代码。尽管设计模式很容易重用,但它们并不是要取代开发人员的工作;它们是开发人员的支持与辅助,提供了与特定应用程序无关的通用解决方案,从而尽量避免那些可能导致 Web 应用程序的开发工作出现重大问题的小漏洞。


与临时方案相比,它们消除了不必要的重复,从而缩减了代码库的整体大小,并让我们的代码更加健壮。


在本文中,我将探讨 7 种最出色和最受欢迎的 JavaScript 设计模式,这些模式主要归为以下三类:创作设计模式、结构设计模式和行为设计模式。


1、构造函数设计模式


这是一种特殊的方法,用于在分配内存后初始化新创建的对象。由于 JavaScript 一般来说是面向对象的,所以它打交道最多的就是对象。于是我打算深入研究对象构造函数。在 JavaScript 中创建新对象有三种方法可用。


以下创建构造函数设计模式的一种方法。


// 创建一个新的空对象var newObject = {};// 创建一个新的空对象var newObject = Object.create(Object.prototype);var newObject = newObject();
复制代码


要访问函数的属性,你需要初始化对象。


const object = new ConstructorObject();
复制代码


上面的 new 关键字告诉 JavaScript,一个 constructorObject 应该充当一个构造函数。这个设计模式并不支持继承。更多细节可以参考这里


2、原型模式


原型模式是基于原型继承的。在这种模式中,被创建的对象充当其他对象的原型。实际上,原型(prototype)是被创建的每个对象构造函数的蓝图。


示例:


var myCar= {name:"Ford Escort",brake:function(){console.log("Stop! I am applying brakes");}Panic : function (){console.log ( "wait. how do you stop thuis thing?")}}// 使用 object create 实个新的例化一 carvar yourCar= object.create(myCar);// 现在它就是另一个的原型了console.log (yourCar.name);]
复制代码


3、模块设计模式


模块(module)设计模式对原型模式做了一些改进。模块模式设置了不同类型的修饰符(私有和公共)。你可以创建相似的函数或属性而不会发生冲突。我们还可以灵活地公开重命名函数。这个设计模式的一个缺陷是无法覆盖(override)外部环境中创建的函数。


示例:


function AnimalContainter () {const container = [];function addAnimal (name) {container.push(name);}function getAllAnimals() {return container;}function removeAnimal(name) {const index = container.indexOf(name);if(index < 1) {throw new Error('Animal not found in container');}container.splice(index, 1)}return {add: addAnimal,get: getAllAnimals,remove: removeAnimal}}const container = AnimalContainter();container.add('Hen');container.add('Goat');container.add('Sheep');console.log(container.get()) //Array(3) ["Hen", "Goat", "Sheep"]container.remove('Sheep')console.log(container.get()); //Array(2) ["Hen", "Goat"]
复制代码


4、单例模式


在仅需要创建一个实例的情况下(例如一个数据库连接),这个模式是必需的。在这个模式中,只能在关闭连接时创建一个实例,或者在打开新实例之前必须关闭已有的实例。此模式也称为严格模式,它的一个缺点是测试时的体验很差,因为它隐藏的依赖项对象很难挑出来进行测试。


示例:


function DatabaseConnection () {let databaseInstance = null;// 追踪特定时间创建实例的数量let count = 0;function init() {console.log(`Opening database #${count + 1}`);// 现在执行操作}function createIntance() {if(databaseInstance == null) {databaseInstance = init();}return databaseInstance;}function closeIntance() {console.log('closing database');databaseInstance = null;}return {open: createIntance,close: closeIntance}}const database = DatabseConnection();database.open(); //Open database #1database.open(); //Open database #1database.open(); //Open database #1database.close(); //close database
复制代码


5、工厂模式


这个模式的创新之处在于,它不需要构造函数就能创建对象。它提供了用于创建对象的通用接口,我们可以在其中指定要创建的工厂(factory)对象的类型。这样一来,我们只需指定对象,然后工厂会实例化并返回这个对象供我们使用。当对象组件设置起来很复杂,并且我们希望根据所处的环境轻松地创建不同的对象实例时,使用工厂模式就是很明智的选择。在处理许多共享相同属性的小型对象,以及创建一些需要解耦的对象时也可以使用工厂模式。


示例:


// Dealer ADealerA = {};DealerA.title = function title() {return "Dealer A";};DealerA.pay = function pay(amount) {console.log(`set up configuration using username: ${this.username} and password: ${this.password}`);return `Payment for service ${amount} is successful using ${this.title()}`;};//Dealer BDealerB = {};DealerB.title = function title() {return "Dealer B";};DealerB.pay = function pay(amount) {console.log(`set up configuration using username: ${this.username}and password: ${this.password}`);return `Payment for service ${amount} is successful using ${this.title()}`;};//@param {*} DealerOption//@param {*} configfunction DealerFactory(DealerOption, config = {}) {const dealer = Object.create(dealerOption);Object.assign(dealer, config);return dealer;}const dealerFactory = DealerFactory(DealerA, {username: "user",password: "pass"});console.log(dealerFactory.title());console.log(dealerFactory.pay(12));const dealerFactory2 = DealerFactory(DealerB, {username: "user2",password: "pass2"});console.log(dealerFactory2.title());console.log(dealerFactory2.pay(50));
复制代码


6、观察者模式


观察者(observer)设计模式在许多对象同时与其他对象集通信的场景中用起来很方便。在观察者模式中不会在状态之间发生不必要的事件 push 和 pull;相比之下,所涉及的模块仅会修改数据的当前状态。


示例:


function Observer() {this.observerContainer = [];}Observer.prototype.subscribe = function (element) {this.observerContainer.push(element);}// 下面是从 container 中移除一个元素Observer.prototype.unsubscribe = function (element) {const elementIndex = this.observerContainer.indexOf(element);if (elementIndex &gt; -1) {this.observerContainer.splice(elementIndex, 1);}}/*** we notify elements added to the container by calling* each subscribed components added to our container*/Observer.prototype.notifyAll = function (element) {this.observerContainer.forEach(function (observerElement) {observerElement(element);});}
复制代码


7、命令模式


最后介绍的是命令(command)模式。命令设计模式将方法调用、操作或请求封装到单个对象中,以便我们可以自行传递方法调用。命令设计模式让我们可以从任何正在执行的命令中发出命令,并将责任委托给与之前不同的对象。这些命令以 run() 和 execute() 格式显示。


(function(){var carManager = {// 请求的信息requestInfo: function( model, id ){return "The information for " + model + " with ID " + id + " is foo bar";},// 现在购买这个 carbuyVehicle: function( model, id ){return "You have successfully purchased Item " + id + ", a " + model;},// 现在 arrange viewingarrangeViewing: function( model, id ){return "You have successfully booked a viewing of " + model + " ( " + id + " ) ";}};})();
复制代码


小结


对于 JavaScript 开发人员来说,使用设计模式的好处很多。设计模式的一些主要优点包括提升项目的可维护性,还能减少开发周期中不必要的工作。JavaScript 设计模式可以为复杂的问题提供解决方案,提升开发速度并提高生产率。但并不能说这些设计模式就可以让开发人员偷懒了。


作为参考,我推荐想要深入了解的读者阅读这本书


英文原文


JavaScript design patterns


2020 年 6 月 30 日 10:363791
用户头像
小智 InfoQ 主编

发布了 396 篇内容, 共 308.3 次阅读, 收获喜欢 1718 次。

关注

评论 2 条评论

发布
用户头像
var newObject = newObject();
是不是少了个空格
2020 年 07 月 10 日 15:04
回复
应该是没少的。
2020 年 07 月 10 日 16:50
回复
没有更多了
发现更多内容

你只修改了2行代码,为什么需要两天时间?

Java架构师迁哥

一个改变世界的“箱子”

阿里巴巴云原生

Docker 阿里云 容器 云原生 k8s

破51项国际榜单纪录!解读华为云擎天架构调度求解引擎

华为云开发者社区

华为 架构 华为云

如何在软件发布计划中自动化语义化版本与变更日志

华为云开发者社区

自动化 工具 发布

BAT等大厂面试复习资料文档整理:ActiveMQ+redis+Spring+高并发多线程+JVM

Java架构之路

Java 程序员 架构 面试 编程语言

聚焦LS-MIMO的四大层面,浅谈5G关键技术

华为云开发者社区

华为 5G 华为云

探营苏州数字人民币试点

CECBC区块链专委会

数字人民币

腾讯云TcaplusDB成为首批通过信通院-键值型内存数据库功能评测的产品,树立内存数据库行业标杆

TcaplusDB

数据库 nosql 腾讯云 腾讯 国产化

终于学完了阿里云大数据架构师推荐的Flink入门与实战PDF

小Q

大数据 flink 学习 编程 面试

即构SDK12月迭代:新增多项质量回调,互动白板、云录制SDK同步更新

ZEGO即构

SpringBoot魔法堂:应用热部署实践与原理浅析

云流

设计原则 框架 spring Boot Starter

阿里巴巴内部发行的“Java技术成长笔记”,清晰的标注着Java程序员应该按照怎样的路线来提升自己,需要去学习哪些技术点。

Java成神之路

Java 程序员 架构 面试 编程语言

基于LiteOS Studio零成本学习LiteOS物联网操作系统

华为云开发者社区

操作系统 物联网 华为云

限时!字节跳动大牛2个月标星达70k的算法笔记,免费开源

周老师

Java 编程 程序员 架构 面试

盘点 2020 | 坚持写技术博客一年能有多少收获!

小傅哥

Java 小傅哥 技术人 盘点2020

有了Git这个功能,再也不需要依赖IDE了!

云流

编程 架构

手把手教你免费获取正版 Jetbrains 全家桶 License

郭旭东

ide JetBrains

工作日志:一文总结HBase从搭建到实操,大家一起进步

小Q

大数据 学习 编程 面试 HBase

海淀区政府携手百度,打造数字政务时代新门户

DT极客

网络入门模拟器:Cisco Packet Tracer 实验教程

网络技术平台

没弄懂这些Java基础,简历上千万别写熟悉:异常+反射+注解+泛型

小Q

Java 学习 编程 面试 基础

LeetCode题解:429. N叉树的层序遍历,BFS,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

3年Java开发经验四面阿里成功定级P6年薪45w+,分享我的阿里面经(含面试题答案)希望对你有所启发!

Java成神之路

Java 程序员 架构 面试 编程语言

赶紧看!阿里架构师必备“绝杀版”Tomact架构笔记堪称绝技

比伯

Java tomcat 编程 架构 程序人生

互联网新规鼓励保险与大数据、区块链等新技术融合!业内呼吁配套产品管理制度尽快出炉

CECBC区块链专委会

互联网金融

我叫你不要重试,你非得重试。这下玩坏了吧?

比伯

Java 编程 架构 面试 程序人生

阿里内部超流行的“SpringBoot+ 微服务指南”,理论与实战双管齐下。有幸从一位在阿里的朋友手里要到一份。

Java成神之路

Java 程序员 架构 面试 编程语言

网易云音乐Java面试题:Mybatis事务+SpringBean+Java锁+Redis

Crud的程序员

Java redis spring 程序员 面试

XMEX交易所系统软件开发|XMEX交易所APP开发

开發I852946OIIO

系统开发

阿里三面惨遭被虐,spring,jvm,mybatis,并发编程等一窍不通

Java架构之路

Java 程序员 架构 面试 编程语言

BATJ面试常被问到的100+题:Spring+微服务+SpringMVC+MyBatis

Java架构之路

Java 程序员 架构 面试 编程语言

开发人员都应该了解的7种JavaScript设计模式-InfoQ