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

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

开发人员将 JavaScript 设计模式作为解决问题( F . + p 8 m的模板是很合适的,但并不是说这些模式可以代替开发人员的工作。
通过设计模式,我们可以将许多开发人员的经验结合起来,以优化过3 K A K的方式来构造I ~ M代码,从而解决我们所面对的问题。设计模式还提供了% M Q d用于描述问题解决方案的通用词汇表,而不是去枯燥地描述代码的语法和语义。

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

与临e U Z U @ F U H f时方案相比,它们消除了不必要的重复,~ X N从而缩减了代码库的整体大小,并让我们的代码更加健壮。
在本文中,我将探c 0 k E ( a讨 7 种最出色和最受 5 _ i欢迎的 JavaScript 设计模式,这些模式主要归为以下三类:创作设计模式、结构设计模式和行为设计模式。

1、构造函数设计模式
这是一种特殊的方法,用于在分配内存后初始化新创建的对象。由于 JavaScript 一般来说` x v j u Q是面向对象的,所以它打交道f ] w 4 } J # q W最多的就是对象。于是我打算深入研究对象构造函数b & z C D ) A s在 JavaScript 中创建新对象有^ c K : y ] z p M三种方法可用。
以下创建构造函数设计模式的一种方法。

// 创建一个新的空对象
va= . h ? h 4 = f Ar newObject = {};
// 创建一个新的空对象
var newObject = Object.create(Object.prototype);
var newObject = newObject();

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

const object = new ConstructorObject();

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

2、原型模式
原型模e 3 7式是基于原型继承的。在这种模式中,被创建的对象充当其他对象的原型。实u g v j h _ s际上,原型(pro$ 7 ^ ct& C t # 3 Wotype)是z # D % s被创建的h & V 3 @每个对象构造函数的蓝图。
示例5 8 B 6 Q q m f i

var myCH e & 5 $ _ar= {
name:"Ford Escort",
brake:fu- m i E ^ t  unction(){
console.log("Stop! I am applying brakes");
}
Panic : function (){
console.log ( "wait. how do you stop thuis thing?")
}
}
// 使用 object create 实个新的例化一 car
var yourCar= object.createh f V(myCR z o v V F I !ar);d X X #
// 现在它就& / k W b m `是另一个的原型了
console.log (yourCar.name);]

3、模块设计模式
模块(module)设计模式对原型模式做了一些改进。模块模式设置了不同类型的修饰符(私有和公共)。你l t x ~ F可以创建相似的函数或属性而不会发生冲突。我们r y h &/ n * & x w Q可以灵活地公开重命名函数。这个设计模式的一个缺陷是无法覆盖(override)外部环境中创建的s s } Z B函数。
示例:

functioni u Q U ^ AnimalContaY 3 & k  1inter# p 3 G F = q ~ () {
const conL B a ( D C e w Atainer = [];
functionM 2 B N 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: adda , 8Animal,
get: getAllAnimals,
remove: removeAnimal
}
}
const container = AnimalContainter();
containy f Mer.add('Hen');
conI ? _tainer.add('Goat')3 m D |;
container.add('Sheep');
console.log(container.get()) //Array(3) ["Hen", "Goat", "Sheep"]
container.remove('Sheep')
consZ P b C S l y ) iole.log(container.get()); //Array(2) [p f J q I [ X"Hen", "Goat"]

4、单例模式
在仅需要创建一个实例的N a O @ c情况下(例如一个数据库连接),这个模式是必需的。在L - N L #这个模式中,只能在关闭连接时创建一个实例,或者在打开新实例3 N ? Z C C S [ L之前必须关闭已有的实例。此模式也* - }称为严格模式,它的一个缺点是测试时的体验很差,因为它隐藏的依赖项对象很难挑出来进行测试。
示例:

function DatabaseConnection () {
let databaseInstance = null;
// 追踪特R [ $ ? 3 & B定时间创建实例的数量
let count = 0;
function init() {
console.log(`Opening database #${count +x % a H L $ e 4 / 1}`);
/= W % t %/ 现在执行操作
}
function createIntance() {
if8 W n S ((databaseInstaO t s d ^ ,  ~nce == null) {F x Z O (
databaseInstance = init();
}
return databaseInstr u , R ~ v * bance;
}
function clos, } p D x ReIntance() {
console.log('closing database');
databaseInstance = null;
}
return {
open: createIntance,
close: closeIntance
}
}
const database = Datab q ? HbseConnection();
database.open(); //Open database #1
database.opey . 3 pn(); //Open database #1
database.open(); //Open database #1
database.close(); //close database

5、工厂模式

这个模式的创新之处在p & 4 5 G [于,它不需要构造函数就能创建对象。它提供了用a { 1于创建对象的通用接口,我们可以在其中指定要创建的工厂(fa! ( g 5 4 e Actor$ z s {y)对象的类型。这样一来,我们只需指定对象,然后工厂会实例化并返回c t U J J = }这个对象供我们使用。当对象组件设置起来很复杂,并且我们希望根据所I ` L f J ; M ;处的环境轻松地创建不同的对象实例时,使用工厂模式就是很明智的选择。在处理许多共享相同属性的小型对象,以及创建一些需要解耦的对象时也可以使用工厂模式。
示例:

// Dealer A
DealerA = {};
DealerA8 % Z , @ m - C.title = function title() {
return "Dealer A";
};
DealerA.pa% 4 # , r o W wy = function pay(amount) {
console.log(
`set up configurat0 E Gion using username: ${Z q E ! h K +this.username} and password: ${
this.password
}`
);
return `PaP ^ myment for service ${amountX L t} is succer  k A G 9 essful using ${thisg 0 n =.title()T Q 3 8 % K}`;) o F P P V
};
//Dealer B
DealerB = {};
DeH v p % malerB.titly C 3 * Ne = function title() {
return "G 3 rDealer B";
};
DealerB.pay = funco % L gtion pay(amount+ O  K u i) {
conso8 4 d ( ~ w }le.log(
`set up configuration usinq e F q d F ! $g username: ${this.username}
and password: ${this.password}`
);
return `Payment for service ${amount} is successful using ${this.title()}`;
};
//@param {*} DealerOption
//@param {*} config
functionz [ x p I L DealerFactory(DealerOption, config = {}) {
const dealer = Object.create(dealerOption);
Object.assign(dealer, config);
return dealer;
}
const dealerFactory = DealerFactory(DealerA, {
username: "usep } mr",
password: "pass"
});
console.log(dealerFactory.title());
console.log(dealerFactory.pay(12))9 r 4;
const dealerFactory2 = DeaN L y k t A _ tlerFacK o F | b 0 jtory(DealerB, {
username: "user2",
password: "p0 a _ Q G Sass2"
});
console.log(deale_ b a - , srFactory2.title());
console.log(dealerFactory2.pay(50));

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

function Observer() {
this.obserE b M 7 + J W ` -verContainer = [];
}8 j T  k ?
Observer.prototype.subscribe = function (element) {
this.obse- u - ) ; Z arverContainer.push(element);
}
// 下面是从 co- O m 8ntainer 中移除一个元素
ObserB ) hver.prototype.unsubscribe = functionR a I + L ( | F (element) {
const elementIndex = this.observerContainer.f A A ) D a =indexOf(element);
iy @ 8f (elementIndex &gt; -1) {
tf d  g d 1his.observerContainer.splice(elementIndex, 1s s Q);
}
}
/**
* we notify eleme8 $ Snts added to the container by calling
* each subsJ c I + 7cribet H P # H E W { Md components addedA u E to our cr l y [ G Montainer
*/
Observer.protT + B Hotype.notifyAll = function (elemj a = ; w B F :ent) {
this.observerContainer.forEach(function (observerElement) {
observerElement(element);
});
}

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

(function(){
var carn z X KManager = {
// 请求的信息
requestInfo: function( model, id ){
return "The information for " + model + " with ID " + id + " is foo bar";
},
// 现在购买这个 car
buyVehicle: function( modeX Z X G 4l, id ){
return "You have successfully purchased Item " + id + ", a " + model;
},
// 现在 arrangU y b ) v ke viewing
arrangeViewing: function4 ; % = l( model, id ){
return "You have successfully booked a viewing of " + moj G s u B W # ? Pdel + " ( " + id + " ) ";k [ N K
}
};
})();

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

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-30
本文作者:Deven
本文来自e @ l 7 } { ] * O:“InfoQ ”,了解相关信息可以关注“[InfoQ](https://www.infoq.cn/article/jUDjSPGGcZx7FKKhWUcc)