策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。目的是将算法的使用与算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成:
- 一组策略类,策略类封装了具体的算法,并且负责具体的计算过程。
- 环境类Context, Context接受客户的请求,随后把请求委托给某一个策略类。
1 | validator = { |
1 | var validator = { |
通过策略模式,消除了大片的条件分支语句。将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。同时,算法还可以复用在其他地方,从而避免许多重复的复制粘贴工作。
代理模式
代理模式为一个对象提供一种代理以控制对这个对象的访问。
虚拟代理是我们最常用的代理模式,它把一些开销很大的对象,延迟到真正需要用到这个对象的时候才去创建
虚拟代理实现图片预加载1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var addImg = (function(){
var img = document.createElement('img');
document.body.appendChild(img);
return {
setSrc: function(src){
img.src = src;
}
}
})();
var proxyAddImg = (function(){
var img = new Image();
img.onload = function(){
addImg.setSrc(this.src);
}
return {
setSrc: function(src){
addImg.setSrc('loading.gif');
img.src = src;
}
}
})();
proxyAddImg.setSrc('demo.png');
虚拟代理合并Http请求
我们可以通过一个代理函数来收集一段时间之内的请求,最后把请求合并到一起发送给服务器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var proxySynData = (function(){
var cache = [], //缓存我们需要同步的内容
timer; //定时器
return function(ID){
if(!timer){ //定时器不存在就创建
timer = setTimeout(function(){
synData(cache.join()); //同步合并后的数据
cache.length = 0; //清空缓存
clearTimeout(timer); //清除定时器
timer = null; //方便垃圾回收
}, 2000);
}
cache.push(ID); //存入缓存
}
})();
var list = document.getElementsByTagName('input');
for(var i = 0, item; item = list[i]; i++){
item.onclick = function(){
if(this.checked){
proxySynData(this.id);
}
};
}
缓存代理
缓存代理就很好理解了,可以缓存一些开销很大的运算结果;如果你第二次执行函数的时候,传递了同样的参数,那么就直接使用缓存的结果,如果运算量很大,这可是不小的优化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var add = function(){
var sum = 0;
for(var i = 0, l = arguments.length; i < l; i++){
sum += arguments[i];
}
return sum;
};
var proxyAdd = (function(){
var cache = {}; //缓存运算结果的缓存对象
return function(){
var args = Array.prototype.join.call(arguments);//把参数用逗号组成一个字符串作为“键”
if(cache.hasOwnProperty(args)){//等价 args in cache
console.log('使用缓存结果');
return cache[args];//直接使用缓存对象的“值”
}
console.log('计算结果');
return cache[args] = add.apply(this,arguments);//使用本体函数计算结果并加入缓存
}
})();
console.log(proxyAdd(1,2,3,4,5)); //15
console.log(proxyAdd(1,2,3,4,5)); //15
console.log(proxyAdd(1,2,3,4,5)); //15
观察者模式
观察者模式又叫发布-订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。
定义一个事件对象,它有以下功能
- 监听事件(订阅公众号)
- 触发事件(公众号发布)
- 移除事件(取订公众号)
1 | // subscription.js |
Redux采用了观察者模式
createStore(rootReducer,initialState,applyMiddleware(thunkMiddleware))
返回值:
(1) dispatch(action): 用于action的分发,改变store里面的state
(2) subscribe(listener): 注册listener,store里面state发生改变后,执行该listener。返回unsubscrib()方法,用于注销当前listener。
(3) getState(): 读取store里面的state
(4) replaceReducer(): 替换reducer,改变state修改的逻辑
所以store内部维护listener数组,用于存储所有通过store.subscribe注册的listener;当store tree更新后,依次执行数组中的listener1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.');
}
var isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
了
return function unsubscribe() {
if (!isSubscribed) {
return;
}
isSubscribed = false;
ensureCanMutateNextListeners();
var index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}
dispatch方法主要完成两件事:
1、根据action查询reducer中变更state的方法,更新store tree
2、变更store tree后,依次执行listener中所有响应函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function dispatch(action) {
if (!(0, _isPlainObject2['default'])(action)) {
throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
}
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
//循环遍历,执行listener,通知数据改变
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
return action;
}
在redux中,我们用connect()方法将react中的UI组件与redux的状态、事件关联起来。
1 | var Connect = function (_Component) { |
装饰者模式
在不改变对象自身的基础上,在程序运行期间给对象动态地添加一些额外职责
1 | // 给window绑定onload事件 |
AOP装饰函数
AOP(Aspect Oriented Programming)面向切面编程
把一些与核心业务逻辑无关的功能抽离出来
再通过“动态织入”方式掺入业务逻辑模块
1 | // 前置装饰 |
1 | //定义一个组件 |
适配器模式
适配器模式是作为两个不兼容的接口之间的桥梁,它结合了两个独立接口的功能。
假设我们正在编写一个渲染广东省地图的页面,目前从第三方资源里获得了广东省的所有城市以及它们所对应的ID,并且成功地渲染到页面中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35var getGuangdongCity = function () {
var GuangdongCity = [
{
name:'shenzhen',
id : '11'
},
{
name:'guangzhou',
id:12
}
];
return GuangdongCity;
};
var render = function (fn) {
console.log('starting render Guangdong map');
document.write(JSON.stringify(fn()));
};
/* var GuangdongCity = {
// shenzhen:11,
// guangzhou:12,
// zhuhai:13
// };
*/
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));
使用适配器模式可以解决参数类型有些许不一致造成的问题。
redux为了和react适配,所有有 mapStateToProps()这个函数来把state转为Props外部状态,这样就可以从外部又回到组件内了