这里就按23中常见设计模式的顺序来展示前端一些常用的设计方法。
本文主要参考 《js设计模式与实践开发》
实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建 过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
Document
单例模式同样也有一些特殊的实现方法
var CreateDiv = function( html ){
this.html = html;
this.init();
};
CreateDiv.prototype.init = function(){
var div = document.createElement( 'div' );
div.innerHTML = this.html;
document.body.appendChild( div );
};
//接下来引入代理类 proxySingletonCreateDiv:
var ProxySingletonCreateDiv = (function(){
var instance;
return function( html ){
if ( !instance ){
instance = new CreateDiv( html );
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv( 'sven1' );
var b = new ProxySingletonCreateDiv( 'sven2' );
alert ( a === b );
将单例实现交给代理类,而把原本的类变成一个普通类。
单例模式的应用主要包括两点,第一是节省资源,避免创建多个实例,第二个就是为了让多个new出来的实例(其实还是一个实例只是多个变量)共同管理同一些状态,比如多个人消费同一笔钱等。
先看一个例子
var calculateBonus = function( performanceLevel, salary ){
if ( performanceLevel === 'S' ){
return salary * 4;
}
if ( performanceLevel === 'A' ){
return salary * 3;
}
if ( performanceLevel === 'B' ){
return salary * 2;
}
};
calculateBonus( 'B', 20000 ); // 输出:40000
calculateBonus( 'S', 6000 ); // 输出:24000
这就是策略模式解决的典型问题,当存在多个if else判断情况时,代码会变得臃肿而且难以管理。
使用策略模式重构
策略模式指的是定义一系 列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策 略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。
//去除大量重复的情况判断。
var strategies = {
"S": function (salary) {
return salary * 4;
},
"A": function (salary) {
return salary * 3;
},
"B": function (salary) {
return salary * 2;
}
};
var calculateBonus = function (level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
代理模式分为两大应用:
第一个是保护代理:即代理会滤掉一些特殊的请求,特殊的条件保证原行为执行的成功性。
第二个是虚拟代理:将一些开销大的操作延迟到需要执行时执行。
保护代理用于控制不同权限的对象对目标对象的访问,但在 JavaScript 并不容易实现保护代 理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式,本章主要讨论 的也是虚拟代理。
代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。
缓存代理
/**************** 计算乘积 *****************/
var mult = function () {
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
/**************** 计算加和 *****************/
var plus = function () {
var a = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a + arguments[i];
}
return a;
};
/**************** 创建缓存代理的工厂 *****************/
var createProxyFactory = function (fn) {
var cache = {};
return function () {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
return cache[args] = fn.apply(this, arguments);
}
};
var proxyMult = createProxyFactory(mult),
proxyPlus = createProxyFactory(plus);
alert(proxyMult(1, 2, 3, 4)); // 输出:24
alert(proxyMult(1, 2, 3, 4)); // 输出:24
alert(proxyPlus(1, 2, 3, 4)); // 输出:10
alert(proxyPlus(1, 2, 3, 4)); // 输出:10
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即 使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
应用不是特别多 给个例子就行
var each = function( ary, callback ){
for ( var i = 0, l = ary.length; i < l; i++ ){
callback.call( ary[i], i, ary[ i ] ); // 把下标和元素当作参数传给 callback 函数
}
};
each( [ 1, 2, 3 ], function( i, n ){
alert ( [ i, n ] );
});
这个模式应该是前端最熟悉的设计模式之一。
这里模拟一个发布订阅模式的简单实现,(包含单例模式,为了让多个变量控制同一个线程池)
Document
命令模式是最简单和优雅的模式之一,命令模式中的命令(command)指的是一个执行某些 特定事情的指令。
宏命令 一条命令执行多个具体任务。
var MacroCommand = function () {
return {
commandsList: [],
add: function (command) {
this.commandsList.push(command);
},
execute: function () {
for (var i = 0, command; command = this.commandsList[i++];) {
command.execute();
}
}
}
};
var macroCommand = MacroCommand();
macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQCommand);
macroCommand.execute();
和发布订阅蛮像的。
组合模式将对象组合成树形结构,以表示“部分整体”的层次结构。 除了用来表示树形结 构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使 用具有一致性。
var closeDoorCommand = {
execute: function(){
console.log( '关门' );
}
};
var openPcCommand = {
execute: function(){
console.log( '开电脑' );
}
};
var openQQCommand = {
execute: function(){
console.log( '登录 QQ' );
}
};
var MacroCommand = function(){
return {
commandsList: [],
add: function( command ){
this.commandsList.push( command );
},
execute: function(){
for ( var i = 0, command; command = this.commandsList[ i++ ]; ){
command.execute();
}
}
}
};
var macroCommand = MacroCommand();
macroCommand.add( closeDoorCommand );
macroCommand.add( openPcCommand );
macroCommand.add( openQQCommand );
macroCommand.execute();
这两个放在一起说是因为他们在js中的应用比较常见,基本都是一起使用。
模板方法指的最明确的例子就是继承,父类是模板,子类为多个使用该模板的例子。
指享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
能复用的属性对象就复用没必要一直创建,典型的例子就是可以使用原型链创建方法而并非直接向构造函数里添加,或者是使用类变量。
职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间 的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
从这两个例子中,我们很容易找到职责链模式的最大优点:请求发送者只需要知道链中的第 一个节点,从而弱化了发送者和一组接收者之间的强联系。如果不使用职责链模式,那么在公交 车上,我就得先搞清楚谁是售票员,才能把硬币递给他。同样,在期末考试中,也许我就要先了 解同学中有哪些可以解答这道题。