javaScript 设计模式回顾

好久之前看过AlloyTeam写的javascript设计模式与开发实践。当时似懂非懂,看的很仔细,但感觉没有对应的工作经验,无法深刻体会到具体的精髓,并不知其所以然。看了一段时间源码后,算是理解了一部分设计模式的精髓。此番回顾一下,重读一遍该书,以加深理解

1. 单例模式

文件只有单一类,所以方法全部聚集在原型链,目前最最常用的,不多说

代理实现单例模式,缓存代理

var ProxySingletonCreateDiv = (function(){
var instance;
return function( html ){
if ( !instance ){
instance = new CreateDiv( html );
}
return instance;
}
})();

2. 策略模式

将具有多态(策略)或者状态发生变化的情况下,将策略转化为对象属性(策略)进行统一管理,同时可以减少很多if,优雅。

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 );
};

应用场景,后台返回状态码、动画曲线库、表单验证规则库

var tween = {
linear: function(t, b, c, d) {
return c * t / d + b;
},
easeIn: function(t, b, c, d) {
return c * (t /= d) * t + b;
},
strongEaseIn: function(t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
},
strongEaseOut: function(t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
},
sineaseIn: function(t, b, c, d) {
return c * (t /= d) * t * t + b;
},
sineaseOut: function(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
};
var strategies = {
isNonEmpty: function(value, errorMsg) { // 不为空
if (value === '') {
return errorMsg;
}
},
minLength: function(value, length, errorMsg) { // 限制最小长度
if (value.length < length) {
return errorMsg;
}
},
isMobile: function(value, errorMsg) { // 手机号码格式
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
...
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' );
validator.add( registerForm.password, 'minLength:6', '密码长度不能少于6 位' );
validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确' );

3. 代理模式

应用:

  • 图片预加载,在加载图片前先用loading.gif来占位。
  • jquery事件代理绑定模式,在一个table的所有td下绑定事件
var myImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image;
img.onload = function() {
myImage.setSrc(this.src);
}
return {
setSrc: function(src) {
myImage.setSrc('file:// /C:/Users/svenzeng/Desktop/loading.gif');
img.src = src;
}
}
})();
// 在加载前可以先用loading.gif来占位。
proxyImage.setSrc('http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg');

4. 迭代器模式

jquery中的迭代器

$.each( [1, 2, 3], function( i, n ){
console.log( '当前下标为: '+ i );
console.log( '当前值为:' + n );
});

5. 观察者模式(发布-订阅)

目前浏览器的各种addEventLisener事件,都是观察者模式的应用
sub-pub模式定义了对象1对多的依赖关系

React中单向事件流,其中就有观察者模式,其中一个state发生变化,订阅state的components都会引起变化

自定义的发布-订阅模式的话:通用实现如下

var event = {
clientList: [],
listen: function(key, fn) {
if (!this.clientList[key]) {
this.clientList[key] = [];
}
this.clientList[key].push(fn); // 订阅的消息添加进缓存列表
},
trigger: function() {
var key = Array.prototype.shift.call(arguments), // (1);
fns = this.clientList[key];
if (!fns || fns.length === 0) { // 如果没有绑定对应的消息
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments); // (2) // arguments 是trigger 时带上的参数
}
}
};

6. 装饰者模式

AOP装饰函数 _.before_.after

7. 适配器模式

主要处理接口不匹配的问题。

用于统一变换接口最方便了。目的就是将一种类型的格式通过函数处理转变另一种格式,例子如下

// 目前的guangdongcity的字段格式如下,字段信息很少
var getGuangdongCity = function() {
var guangdongCity = [{
name: 'shenzhen',
id: 11,
}, {
name: 'guangzhou',
id: 12,
}];
return guangdongCity;
};
var render = function(fn) {
console.log('开始渲染广东省地图');
document.write(JSON.stringify(fn()));
};
render(getGuangdongCity);
...
// 但是突然有一天,收集到了一个完整的信息map,并且格式也发送了很大变化,如下
var guangdongCity = {
shenzhen: 11,
guangzhou: 12,
zhuhai: 13,
...
};

想当初自己就是苦逼的一个一个改,包括后续获取数据的接口可能都要变化。这样效率极低,而且很容易出错。

更轻便的方法就是写一个数据格式的转换器

var getGuangdongCity = function() {
var guangdongCity = [{
name: 'shenzhen',
id: 11,
}, {
name: 'guangzhou',
id: 12,
}];
return guangdongCity;
};
var render = function(fn) {
console.log('开始渲染广东省地图');
document.write(JSON.stringify(fn()));
};
var addressAdapter = function(oldAddressfn) {
var address = {},
oldAddress = oldAddressfn();
for (var i = 0, c; c = oldAddress[i++];) {
address[c.name] = c.id;
}
return function() {
return address;
}
};
render(addressAdapter(getGuangdongCity));
0%