js设计模式系列(1)----此文主要是对<<JavaScript设计模式与开发实践>>一书的范例摘要。
一、单例模式
1、js没有类的概念,如果是字面量对象的方式,那么直接创建的字面量对象就是一个单例对象,如果我们需要兼顾构造函数创建对象以及惰性加载的方式,就可以采用闭包缓存已经创建的对象,详见下面案例:
var getSingle = function(fn) { // fn是创建对象的方法
var result ;
return function() {
return result || (result = fn.apply(this, arguments)) //result不存在,就创建
}
}
// 调用getSingle方法,传入一个创建对象的方法。
var getInstance = getSingle(function(str) {
return Symbol(str);
});
// getInstance 就可以用来创建对象
var a1 = getInstance('div');
var a2 = getInstance('div');
console.log(a1 == a2); // 结果为true
console.log(Symbol('xx')===Symbol('xx')); // false ,如果创建了二次Symbol对象就肯定是false,这说明只创建了一次symbol对象。
二、策略模式
策略模式,就是定义一系列的算法,把每一个算法封装成策略类,并且可以使他们互相替换,我们先看看类java预言实现的策略模式
// 2倍绩效的策略(类)
var performanceTwo = function () { }
performanceTwo.prototype.calculate = function (salary) {
return salary * 2;
}
// 3倍绩效的策略(类)
var performanceThree = function () { };
performanceThree.prototype.calculate = function (salary) {
return salary * 3;
}
// 管理类 (使用策略类的调用者)
var Manager = function (salary) {
this.salary = salary; // 原始工资
this.strategy = null;
};
// 设置策略对象
Manager.prototype.setStrategy = function (strategy) {
this.strategy = strategy;
};
// 得到最终工资 : 使用传入的策略对原始工资作为参数进行计算。
Manager.prototype.getBonus = function () {
return this.strategy.calculate(this.salary);
};
var manager = new Manager(1000);
manager.setStrategy(new performanceThree());
console.log(manager.getBonus())
manager.setStrategy(new performanceTwo());
console.log(manager.getBonus());
2、js方法本身就可以作为参数进行传递,即定义不同算法的方法可以随意替换,只要返回值和参数类型符合运行不报错即可,下面演示js的策略模式。
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);
};
上面把定义策略方法用字符串作为策略组的key,然后调用时,传key值就可以取到对应的策略方法。
三·、代理模式
其实上面单例模式就是利用了代理模式,扩展了创建对象方法的缓存能力,下面实现一个利用js代理模式缓存方法执行结果的案例。
// 创建缓存方法执行结果的代理方法(类)
var createProxyFn = 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 mult = function () {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return a;
};
// 加法函数
var plus = function () {
var a = 0;
for (var i = 0; i < arguments.length; i++) {
a = a + arguments[i];
}
return a;
};
// 测试
var proxyMult = createProxyFn(mult);
var proxyPlus = createProxyFn(plus);
console.log(proxyMult(1, 2, 3, 4)); // 输出:24
console.log(proxyMult(1, 2, 3, 4)); // 输出:24
console.log(proxyPlus(1, 2, 3, 4)); // 输出:10
console.log(proxyPlus(1, 2, 3, 4)); // 输出:10
createProxyFn 方法,就是根据arguments参数拼接标记id,如果此id的结果已经计算过,那么从缓存中取,否则,重新计算并且把结果赋值给对应id的缓存属性。
四、迭代器
// 迭代器模式
// 1、显示迭代的方式,即把迭代元素遍历,用一个函数参数进行处理。
var each_one = function (arr, fn) {
for (let i = 0; i < arr.length; i++) {
fn(arr[i], i);
}
}
// 显示迭代的使用
each_one([1, 2, 3], function (obj, index) {
console.log(obj, index);
});
// 2、外部迭代器:主要是闭包内维护一个pos变量,来判断获取当前迭代的对象。
var Interator = function (obj) {
var pos = 0;
var next = function () {
pos += 1;
}
var hasNext = function () {
pos >= obj.length;
}
var getCurrent = function () {
return obj[pos];
};
return {
next,
hasNext,
getCurrent
}
}
// 外部迭代器的使用
var interator = Interator([1, 2, 3]);
while (interator.hasNext()) {
interator.next();
console.log(interator.getCurrent());
};
// 3、jquery 迭代器 ()
$.each = function (obj, callback) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike(obj);
if (isArray) { // 迭代类数组
for (; i < length; i++) {
value = callback.call(obj[i], i, obj[i]);
if (value === false) {
break;
}
}
} else {
for (i in obj) { // 迭代 object 对象
value = callback.call(obj[i], i, obj[i]);
if (value === false) {
break;
}
}
}
return obj;
};