三、代理模式
1. 定义
为一个对象提供一个代用品或占位符,以便控制对它的访问
2. 核心
当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。
替身对象对请求做出一些处理之后, 再把请求转交给本体对象
代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情
3. 实现
代理模式主要有三种:保护代理、虚拟代理、缓存代理
保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子
js
体验AI代码助手
代码解读
复制代码
// 主体,发送消息
function sendMsg(msg) {
console.log(msg);
}
// 代理,对消息进行过滤
function proxySendMsg(msg) {
// 无消息则直接返回
if (typeof msg === 'undefined') {
console.log('deny');
return;
}
// 有消息则进行过滤
msg = ('' + msg).replace(/泥\s*煤/g, '');
sendMsg(msg);
}
sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀
proxySendMsg('泥煤呀泥 煤'); // 呀
proxySendMsg(); // deny
它的意图很明显,在访问主体之前进行控制,没有消息的时候直接在代理中返回了,拒绝访问主体,这数据保护代理的形式
有消息的时候对敏感字符进行了处理,这属于虚拟代理的模式
虚拟代理在控制对主体的访问时,加入了一些额外的操作
在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数节流,这是一种虚拟代理的实现
js
体验AI代码助手
代码解读
复制代码
// 函数防抖,频繁操作中不处理,直到操作完成之后(再过 delay 的时间)才一次性处理
function debounce(fn, delay) {
delay = delay || 200;
var timer = null;
return function() {
var arg = arguments;
// 每次操作时,清除上次的定时器
clearTimeout(timer);
timer = null;
// 定义新的定时器,一段时间后进行操作
timer = setTimeout(function() {
fn.apply(this, arg);
}, delay);
}
};
var count = 0;
// 主体
function scrollHandle(e) {
console.log(e.type, ++count); // scroll
}
// 代理
var proxyScrollHandle = (function() {
return debounce(scrollHandle, 500);
})();
window.onscroll = proxyScrollHandle;
缓存代理可以为一些开销大的运算结果提供暂时的缓存,提升效率
来个栗子,缓存加法操作
js
体验AI代码助手
代码解读
复制代码
// 主体
function add() {
var arg = [].slice.call(arguments);
return arg.reduce(function(a, b) {
return a + b;
});
}
// 代理
var proxyAdd = (function() {
var cache = [];
return function() {
var arg = [].slice.call(arguments).join(',');
// 如果有,则直接从缓存返回
if (cache[arg]) {
return cache[arg];
} else {
var ret = add.apply(this, arguments);
return ret;
}
};
})();
console.log(
add(1, 2, 3, 4),
add(1, 2, 3, 4),
proxyAdd(10, 20, 30, 40),
proxyAdd(10, 20, 30, 40)
); // 10 10 100 100
四、迭代器模式
1. 定义
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
2. 核心
在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素
3. 实现
JS中数组的map forEach 已经内置了迭代器
js
体验AI代码助手
代码解读
复制代码
[1, 2, 3].forEach(function(item, index, arr) {
console.log(item, index, arr);
});
不过对于对象的遍历,往往不能与数组一样使用同一的遍历代码
我们可以封装一下
js
体验AI代码助手
代码解读
复制代码
function each(obj, cb) {
var value;
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; ++i) {
value = cb.call(obj[i], i, obj[i]);
if (value === false) {
break;
}
}
} else {
for (var i in obj) {
value = cb.call(obj[i], i, obj[i]);
if (value === false) {
break;
}
}
}
}
each([1, 2, 3], function(index, value) {
console.log(index, value);
});
each({a: 1, b: 2}, function(index, value) {
console.log(index, value);
});
// 0 1
// 1 2
// 2 3
// a 1
// b 2
再来看一个例子,强行地使用迭代器,来了解一下迭代器也可以替换频繁的条件语句
虽然例子不太好,但在其他负责的分支判断情况下,也是值得考虑的
js
体验AI代码助手
代码解读
复制代码
function getManager() {
var year = new Date().getFullYear();
if (year <= 2000) {
console.log('A');
} else if (year >= 2100) {
console.log('C');
} else {
console.log('B');
}
}
getManager(); // B
将每个条件语句拆分出逻辑函数,放入迭代器中迭代
js
体验AI代码助手
代码解读
复制代码
function year2000() {
var year = new Date().getFullYear();
if (year <= 2000) {
console.log('A');
}
return false;
}
function year2100() {
var year = new Date().getFullYear();
if (year >= 2100) {
console.log('C');
}
return false;
}
function year() {
var year = new Date().getFullYear();
if (year > 2000 && year < 2100) {
console.log('B');
}
return false;
}
function iteratorYear() {
for (var i = 0; i < arguments.length; ++i) {
var ret = arguments[i]();
if (ret !== false) {
return ret;
}
}
}
var manager = iteratorYear(year2000, year2100, year); // B