目录
描述:
通过在对象方法中将对象返回, 实现对同一个对象多个方法的链式调用。简化对该对象的多个方法的多次调用时,对该对象的多次引用。
最典型的使用就是jQuery
简单的来说, 这种设计模式就是封装一个对象, 然后对象方法处理数据, 数据就放在对象的属性上面, 然后方法返回this指针, 就可以了。
来看一个实际使用的一个实例:
/*
* sum(1).sum(1,2).sum(3,4).sum(2).sum(2,3,4,5,6,7,8).result() // 48
* */
class Test {
constructor() {
this.resultNumber = 0;
}
sum() {
let args = arguments,
len = args.length;
if (len > 0) {
for (let num of args) {
this.resultNumber += num;
}
}
return this;
}
result() {
return this.resultNumber;
}
}
let test = new Test();
let resultNumber = test.sum(1).sum(1, 2).sum(3, 4).sum(2).sum(2, 3, 4, 5, 6, 7, 8).result();
console.log(resultNumber); // 48
其他的示例:
01、原型式继承存在的问题
02、对于类jQuery链式调用模式的研究
03、链式模式的一个实际使用实例
多个对象接受并处理同一个请求, 他们请求委托给另一个对象统一处理请求。
这种设计模式是只针对于浏览器端的来使用的, 至于node没有dom, 所以没有这个特性。
第一个场景
日历模块, 用户点击每个日期格子的时候将格子的背景色变为灰色, 大多数的做法就是把每个日期格子都绑定一个事件, 做法如下:
let ul = document.getElementById('container');
let li = ul.getElementById('li'),
len = li.length - 1;
for (; len >= 0; len--) {
li[i].onclick = function () {
this.style.backgroundColor = 'gery';
}
}
委托模式实际上就是讲事件委托给更高层面上的肤元素去绑定执行。
let ul = document.getElementById('container');
ul.onclick = function (e = window.event) {
let tar = e.target || e.srcElement;
if (tar.nodeName.toLowerCase() === 'li') {
tar.style.color = 'blue';
}
}
第二个场景
还有一种使用场景: 就是比如我们的dom 是动态添加的, 我们可以把它触发的时间, 暂时委托给父级。 这样的jquery中非常的常见。
$(document).on('click', '#target', function() {
// 处理逻辑
})
第三个场景
委托模式可以解决一些内存泄漏的问题。
<div id="container">
<button id="btn">dom</button>
</div>
<script>
/*
* 下面一段代码中, g变量中保存了元素绑定的click事件没有清楚, 这个时间就会泄露到内存中去
* 失去了对其的控制
* */
let g = function (id) {
return document.getElementById(id);
};
g('btn').onclick = function () {
g('container').innerHTML = '触发了事件'
};
/*
* 利用委托模式解决上面所面临的问题
* */
g('container').onclick = function (e = window.event.srcElement) {
let target = e && e.target;
if(target.id === 'btn') {
g('container').innerHTML = '触发了事件'
}
}
</script>
01、实际场景-点击日期格子变色
02、实际场景-处理内存泄漏问题
抽象和封装对数据源的访问与存储。
场景一: 本地存储 localStorage
class BaseLocalStorage {
constructor(preId, timeSign = '|-|') {
this.preId = preId;
this.timeSing = timeSign;
this.status = {
SUCCESS: 0, // 成功
FAILURE: 1, // 失败
OVERFLOW: 2, // 溢出
TIMEOUT: 3, // 过期
};
this.storage = localStorage || window.localStorage;
}
// 获取本地存储数据库数据真实字段
getKey(key) {
return this.preId + key;
}
/**
* 添加或者修改数据
* @param key 数据字段标识
* @param value 数据值
* @param callback 回到函数
* @param time 添加时间
*/
set(key, value, callback, time) {
// 默认操作状态是成功的
let status = this.status.SUCCESS,
getKey = this.getKey(key);
try {
time = +new Date(time) || time.getTime();
} catch (e) {
// 传入的时间参数或者时间参数有无获取默认时间, 一个月
time = +new Date() + 1000 * 60 * 60 * 24 * 31;
}
try {
this.storage.setItem(getKey, time + this.timeSing + value);
} catch (e) {
// 溢出失败, 返回溢出状态
status = this.status.OVERFLOW;
}
callback && callback.call(this, status, getKey, value);
}
/**
* 获取数据
* @param key 数据字段标识
* @param callback 回调函数
* @returns {*}
*/
get(key, callback) {
let status = this.status.SUCCESS,
getKey = this.getKey(key),
value = null, // 默认值为空
timeSignLen = this.timeSing.length, // 时间戳与存储数据之间的拼接长度
index, // 时间戳与春出数据之间的拼接符其实位置
time, // 时间戳
result; // 最终获取到的数据
try {
value = this.storage.getItem(getKey);
} catch (e) {
result = {
status: this.status.FAILURE,
value: null
};
callback && callback.call(this, result.status, result, value);
return result;
}
// 获取成功
if (value) {
index = value.indexOf(this.timeSing);
time = +value.slice(0, index); // 获取时间戳
if (+new Date(time) > +new Date() || time === 0) {
// 获取数据结果(拼接后面的字符串)
value = value.slice(index + timeSignLen);
} else {
// 获取则结果为null
value = null;
status = this.status.TIMEOUT;
time.remove(key);
}
} else {
status = this.status.FAILURE;
}
// 设置结果
result = {
status: status,
value: value
};
callback && callback.call(this, result.status, result.value);
return result;
}
/**
* 删除数据
* @param key 数据字段标识
* @param callback 回调函数
*/
remove(key, callback) {
let status = this.status.FAILURE,
getKey = this.getKey(key),
value = null;
value = this.storage.getItem(getKey);
if (value) {
// 删除数据
this.storage.removeItem(getKey);
status = this.status.SUCCESS;
}
callback && callback.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSing) + this.timeSing.length))
}
}
/*使用实例*/
let LS = new BaseLocalStorage('LS__');
LS.set('a', 'xiao ming', function () {
console.log(arguments)
});
LS.get('a', function () {
console.log(arguments)
});
LS.remove('a', function () {
console.log(arguments)
});
LS.remove('a', function () {
console.log(arguments)
});
LS.get('a', function () {
console.log(arguments)
});
场景二: 对于mongodb 的使用情况
对于复杂的业务逻辑进行节流控制, 执行最后一次操作并取消其他操作, 提高性能。
场景一: 返回顶部
返回顶部按钮添加动画, 每次拖动页面滚动时, 他都要不停的抖动。 原因是拖动页面滚动条件是, 不停的出发了scroll 时间, 所以返回东部按钮不听的执行动画。
let throttle = function () {
let isClear = arguments[0], fn;
// 第一个参数表示是否清楚计时器
if (typeof isClear === 'boolean') {
// 第二个参数则为函数
fn = arguments[1];
fn.__trottleID && clearTimeout(fn.__trottleID);
} else {
// 第一个参数为函数
fn = isClear;
// 第二个参数为函数执行时候的参数
let param = arguments[1];
let p = Object.assign({
context: null,
args: [],
time: 300
}, param);
arguments.callee(true, fn);
fn.__trottleID = setTimeout(function () {
fn.apply(p.context, p.args)
}, p.time)
}
};
// 实际使用
function moveScroll() {
let top = $(document).scrollTo();
$('#back').animate({top: top + 30}, 400, 'easeOutCubic')
}
// 监听页面滚动事件
$(window).on('scroll', function () {
throttle(moveScroll);
})
场景二: 优化浮层
场景三: 图片的延迟加载
场景四: 统计打包
很简单, 就是类似于一个模板引擎而已, 更多的时候, 可以多考虑一下, 一个模板引擎的设计模式;
略。。。。。。。
减少每次代码执行的重复性的分支判断, 通过对对象重定义来拼比原对象中的分支判断。
场景一:
解决函数执行时候的重复性的分支判断。
A.on3 = function (dom, type, fn) {
if (document.addEventListener) {
A.on3 = function (dom, type, fn) {
dom.addEventListener(type, fn, false);
}
} else if (document.attachEvent) {
A.on3 = function (dom, type, fn) {
dom.attachEvent('on' + type, fn);
}
} else {
A.on3 = function (dom, type, fn) {
dom['on' + type] = fn;
}
}
A.on3(dom, type, fn);
};
代码示例: 场景1-解决重复性的分支判断
场景二:
创建XHR场景
let createXHR2 = function () {
if (typeof XMLHttpRequest !== 'undefined') {
createXHR2 = function () {
return new XMLHttpRequest();
}
} else if (typeof ActiveXObject !== 'undefined') {
createXHR2 = function () {
if (typeof arguments.callee.activeXString !== 'string') {
let versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'],
i = 0,
len = versions.length;
for (; i < len; i++) {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
}
}
}
} else {
createXHR2 = function () {
throw new Error('您的浏览器不支持Ajax.');
}
}
return createXHR2;
}
在特定的作用于中执行给定的函数, 并将参数原封不动的传递。
场景一:
天气模块, 打开页面后定时从后端拉去数据, 缓存下来, 一旦用户点击查看天气按钮, 就展开天气模块, 并现实天气信息。
这个场景比价复杂, 可以一步一步看示例代码: 场景1
通过对多个异步进程的监听, 来触发未来发生的动作。
场景1:
接口拆分, 以前的新闻搜索接口拆分为新闻搜索接口和新闻推荐结果接口。
解决这个情况的急要用到等待这模式, 监听两个异步请求的结果, 然后根据之前的分发逻辑执行就可以了。
等待着模式就是解决那些不确定先后完成的异步逻辑。
监听的是所有的异步逻辑完成, 这样才会自动执行成功回调函数, 如果有一个异步函数执行失败了,那么就执行失败回调函数。
这点儿很类似于promise.all方法
场景1-等待对象
场景2-封装一个异步请求