专注于处理逻辑的内容,实现代码层面更好的解耦和性能优化。
链模式
通过在对象方法中将当前对象返回,实行对同一个对象多个方法的链式调用,简化对该对象多个方法的多次调用
// 参考jQuery链式调用
let A = function (selector) {
return new A.fn.init(selector);
};
A.fn = A.prototype = {
constructor: A,
init: function (selector) {
this[0] = document.getElementById(selector);
this.length = 1;
return this;
},
length: 0,
size: function () {
return this.length;
},
//增强数组
//javascript是弱类型语言,函数、数组、对象都被看成对象的实例
//浏览器引擎解析时判断对象是否数组时,不仅判断是否具备length属性、可否同过【索引值】访问元素,还会判断是否具备数组方法
push: [].push,
sort: [].sort,
splice: [].splice,
};
A.fn.init.prototype = A.fn;
console.log(A('test')); // init [div#test]
委托模式
多个对象接受并处理同一个请求时,将请求委托给另外一个对象统一处理
委托者模式解决了请求与委托者之间的耦合,被委托者处理后接受的请求后,分发给相应的委托者去处理
// 点击单元格时背景色变成灰色
// js的垃圾回收机制是为了防止内存泄漏(不需要的某块内存还存在),垃圾回收机制就是不停歇的寻找这些不再使用的变量,并且释放掉它所指向的内存
// 利用事件冒泡机制,将多个子元素的请求委托给父元素
const ul = document.getElementById('list');
ul.onclick = function (e) {
let event = e || window.event,
target = e.target || e.srcElement;
if (target) {
tar.style.backgroundColor = 'gray';
}
};
// 多个业务请求进行封装,分发给相应的委托者进行处理
let Deal = {
one: function () {
// ...
},
two: function () {
// ...
},
three: function () {
// ...
},
};
getDealData.then((res) => {
// 数据包拆分
for (const i in res) {
Deal[i] && Deal[i](res[i]);
}
});
数据访问对象模式
抽象和封装对数据源的访问与存储,DAO(date access object)通过对数据源链接管理,方便对数据的访问与存储
方便了对于前端存储的的管理(事务型数据库),统一使用方式,并且使用时更加方便
// 封装一个本地数据库
let BaseLocalStorage = function (preId, timeSign) {
// 本地数据库前缀
this.preId = preId;
// 时间戳和存储数据的连接符
this.timeSign;
};
BaseLocalStorage.prototype = {
// 操作状态
status: {
FAil: 0,
SUCCESS: 1,
OVERFLOW: 2,
TIMEOUT: 3,
},
storage: localStorage || window.localStorage,
getKey: function (key) {
return this.preId + key;
},
set: function (key, value, callback, time) {
let dataStatus = this.status.SUCCESS,
dataKey = get(key);
try {
time = new Date().getTime() || time.getTime();
} catch (e) {
time = new Date().getTime() + 1000 * 60 * 60 * 24 * 31;
}
try {
this.storage.setItem(dataKey, time + this.timeSign + value);
} catch (e) {
dataStatus = this.status.OVERFLOW;
}
callback && callback.call(this, dataStatus, dataKey, value);
},
get: function (key, callback) {
let dataStatus = this.status.SUCCESS,
dataKey = get(key),
value = null,
timeSignLen = this.timeSign.length,
that = this,
index,
time;
result;
try {
value = that.storage.getItem(key);
} catch (e) {
result = {
status: that.status.FAIL,
value: null,
};
callback && callback.call(this, result.status, result.value);
return result;
}
if (value) {
index = value.indexOf(that.timeSign);
time = +value.slice(0, index);
// 如果时间未过期
if (new Date().getTime(time) > new Date().getTime() || time == 0) {
value = value.slice(index + timeSigLen);
} else {
value = null;
dataStatus = this.status.TIMEOUT;
that.remover(key);
}
} else {
dataStatus = this.status.FAIL;
}
result = {
status: dataStatus,
value: value,
};
callback && callback.call(this, result.status, result.value);
return result;
},
remove: function (key, callback) {
let dataStatus = this.status.SUCCESS,
dataKey = get(key),
value = null;
try {
value = that.storage.getItem(key);
} catch (e) {}
if (value) {
try {
this.storage.remove(key);
dataStatus = this.status.SUCCESS;
} catch (e) {}
}
callback &&
callback.call(
this,
dataStatus,
dataStatus > 0
? null
: values.slice(value.indexOf(this.timeSign) + this.timeSign.length)
);
},
};
// 使用nodejs创建mongoDB数据库
节流模式
对重复的业务逻辑进行节流控制,执行最后一次操作并取消其他操作,以提高性能
构造节流器有两件事:能够清除将要执行的函数、能够延迟执行函数
节流器应用场景:时间段n内执行一次事件,即控制事件触发频率
类似的还有防抖:时间间隔n执行一次事件,即减少重复事件触发,可参考lodash
function extend(target, source) {
// 遍历源对象属性
for (const i in source) {
target[i] = source[i];
}
return target;
}
// 节流器
const throttle = function () {
// 可使用解构代替 或设置形参数代替
let isClear = arguments[0],
fn;
if (typeof isClear === 'boolean') {
fn = arguments[1];
fn._throttleID && clearTimeout(fn._throttleID);
} else {
fn = isClear;
let param = arguments[1];
let p = extend({ const: null, args: [], time: 300 }, param);
// 清除执行函数句柄
arguments.callee(true, fn);
fn._throttleID = setTimeout(() => {
fn.apply(p.context, p.args);
}, p.time);
}
};
// 图片延迟加载,加载图片过多时占用线程,影响其他文件加载
// 图片篇幅过多时,监听scroll或resize事件加载可视范围内的图片
// 声明一个节流延迟加载图片类
function LazyLoad(id) {
this.container = document.getElementById(id);
this.imgs = this.getImgs();
this.init();
}
LazyLoad.prototype = {
// 初始化图片加载
// 绑定窗口事件
init: function () {
this.update();
this.binEvent();
},
// 获取延迟加载的图片
getImgs: function () {
const imgs = this.container.getElementsByTagName('img');
const arr = Array.from(imgs);
return arr;
},
update: function () {
if (!this.imgs.length) {
return;
}
let i = this.imgs.length;
for (--i; i >= 0; i--) {
if (this.shouldShow(i)) {
// 加载图片
this.imgs[i].src = this.imgs[i].getAttribute('data-src');
// 清除缓存
this.imgs.splice(i, 1);
console.log(this.img);
}
}
},
shouldShow: function (i) {
let img = this.imgs[i],
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
scrollBottom = scrollTop + document.documentElement.ClientHeight;
imgTop = this.pageY(img);
imgBottom = imgTop + img.offsetHeight;
if (
(imgBottom > scrollTop && imgBottom < scrollBottom) ||
(imgTop > scrollTop && imgTop < scrollBottom)
) {
return true;
} else {
return false;
}
},
pageY: function (el) {
return el.offsetParent ? el.offsetTop + this.pageY(el.offsetParent) : el.offsetTop;
},
on: function (el, type, fn) {
// 兼容IE,事实上IE已经被微软废弃,以后类似例子应当不会出现了
el.addEventListener
? el.addEventListener(type, fn, false)
: el.attachEvent('on' + type, fn, false);
},
binEvent: function () {
const that = this;
this.on(window, 'resize', function () {
throttle(that.update, { context: that });
});
this.on(window, 'scroll', function () {
throttle(that.update, { context: that });
});
},
};
简单模板模式
通过格式化字符串拼凑出视图避免创建视图时大量节点操作,优化内存开销
// 创建一个模板生成器,利用es6的模板字符串
const templateGenerator = function (name,data) {
const view = {
code: `<pre><code>${data.code}</code><pre>`,
img: `<img src="${data.src}" alt="${data.alt}" title="${data.title}>`,
// 组合模板
them: ['<div>',`<h1>${data.h1}</h1>`,`${data.content}`'</div>'].join()
}
return view[name]
}
惰性模式
减少每次代码的分支重复性判断,重定义对象来屏蔽分支判断(类似机器学习)
// 第一种,在页面加载时执行,确定时占用一定资源,降低页面渲染速度
// 第二种,在第一种的基础上做一次延迟执行(惰性执行)
/** 单体式命名空间 */
let A = {};
A.show = function (prefix, data) {
if (prefix === '-webkit-') {
A.show = function () {
console.log('-webkit-' + data.name);
};
} else if (prefix === '-moz-') {
A.show = function () {
console.log('-moz-' + data.name);
};
} else if (prefix === '-o-') {
A.show = function () {
console.log('-o-' + data.name);
};
} else {
A.show = function () {
console.log('-any-' + data.name);
};
}
A.show(prefix, data);
};
参与者模式
在特定的作用域中执行给定的函数,并将参数原封不动地传递
// 通过寄生简单实现函数绑定 bind
function bind(fn, context) {
// 闭包返回新函数
return function () {
// 对 fn 装饰并返回
return fn.apple(context, arguments);
};
}
// 借助函数柯里化实现给运行的函数添加额外的自定义数据
// 函数柯里化:对函数参数进行分割,根据传递的参数不同,可以让一个函数存在多种状态(多态扩展)
function curry(fn) {
let _slice = [].slice;
// 从原参数第二个参数开始算起
let args = _slice.call(arguments, 1);
// 闭包返回新函数
return function () {
// 类数组转换为数组
let addArgs = _slice.call(arguments),
//拼接参数
allArgs = args.concat(addArgs);
// 返回新函数
return fn.apply(null, allArgs);
};
}
// 加法器
function add(num1, num2) {
return num1 + num2;
}
// 加 5 加法器
function add5(num) {
return add(5, num);
}
// 使用柯里化创建加 5 加法器
const _add5 = curry(add, 5);
// 增加了不固定的参数,扩大了使用范围,就是函数反柯里化
// 反柯里化函数
Function.prototype.uncurry = function () {
let that = this;
return function () {
/**
--> apply具有分解参数的作用,这很重要
apply把arguments分解成了args1, args2, ...
--> that.call(args1, args2, args3, ...);
--> args1.that(args2, args3, ...);
*/
// 检验当前类型
return Function.prototype.call.apply(that, arguments);
};
};
// 保存数组 push 方法
let push = [].push.uncurry();
let testArray = {};
push(testArray, 'first data', 'second data');
console.log(testArray);
// 使用 object.prototype.toString 校验对象类型
let toString = Object.prototype.toString.uncurry();
console.log(toString(function () {}));
等待者模式
通过对多个异步进程监听,来触发未来发生的动作
等待者对象无需实时监听异步逻辑的完成,只需要在注册监听的异步逻辑状态发生改变时,对所有异步逻辑的状态迭代确认
// 等待者对象,类似es6的Promise.all,es7的async await
const Waiter = function () {
let container = [],
doneArr = [],
failArr = [],
slice = Array.prototype.slice,
that = this;
let PromiseData = function () {
this.resolved = false;
this.rejected = false;
};
PromiseData.prototype = {
resolve: function () {
this.resolved = true;
if (!container.length) {
return;
}
for (let i = container.length - 1; i >= 0; i--) {
// 如果有任意一个监控对象没有 被解决 或 解决失败 则返回
if ((container[i] && !container[i].resolved) || container[i].rejected) {
return;
}
container.splice(i, 1);
}
// 只执行一次
_exec(doneArr);
},
reject: function () {
this.rejected = true;
// 如果没有监控对象则返回
if (!container.length) {
return;
}
// 清空所有监控对象
container.splice(0);
_exec(failArr);
},
};
// 创建监控对象
that.Deferred = function () {
return new PromiseData();
};
// 执行回调函数
function _exec(arr) {
console.log('exec');
for (const i of arr) {
try {
i && i();
} catch (err) {}
}
}
that.when = function () {
container = slice.call(arguments);
// 向前遍历对象
for (let i = container.length - 1; i >= 0; i--) {
// 如果有监控对象 不存在 或 被解决 或不是监控对象
if (
!container[i] ||
container[i].resolved ||
container[i].rejected ||
!(container[i] instanceof PromiseData)
) {
container.splice(i, 1);
}
}
return that;
};
that.done = function () {
doneArr = doneArr.concat(slice.call(arguments));
return that;
};
that.fail = function () {
failArr = failArr.concat(slice.call(arguments));
return that;
};
};
// example
const waiter = new Waiter();
const first = (function () {
const p = waiter.Deferred();
setTimeout(() => {
console.log('first');
p.resolve();
});
return p;
})();
const second = (function () {
const p = waiter.Deferred();
setTimeout(() => {
console.log('second');
p.resolve();
});
return p;
})();
const third = (function () {
const p = waiter.Deferred();
setTimeout(() => {
console.log('third');
p.resolve();
});
return p;
})();
// 可参考jQuery的 Deferred 对象: $.ajax('http://xxx').done(()=>{}).fail(()=>{})
waiter.when(first, second, third).done(function () {
console.log('success');
});
// 简易 es6 Promise 实现
const PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.value = null;
self.error = null;
self.status = PENDING;
// 绑定多个同步回调
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
const resolve = (value) => {
if (self.status != PENDING) return;
setTimeout(() => {
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach((callback) => callback(self.value));
});
};
const reject = (value) => {
if (self.status != PENDING) return;
setTimeout(() => {
self.status = REJECTED;
self.error = value;
self.onRejectedCallbacks.forEach((callback) => callback(self.value));
});
};
executor(resolve, reject);
}
// 拆解 MyPromise 的过程,其中的【递归调用】传入的 resolve 和 reject 控制的是最开始传入的 bridgePromise 的状态
function resolvePromise(bridgePromise, x, resolve, reject) {
if (x instanceof MyPromise) {
if (x.status === PENDING) {
x.then(
(y) => {
resolvePromise(bridgePromise, y, resolve, reject);
},
(error) => {
reject(error);
}
);
} else {
x.then(resolve, reject);
}
} else {
resolve(x);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
// 处理 then 中的两个参数不传的情况
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: (error) => {
throw error;
};
let bridgePromise;
let self = this;
// 返回新的MyPromise对象——bridgePromise
if (this.status === PENDING) {
return (bridgePromise = new MyPromise((resolve, reject) => {
self.onFulfilledCallbacks.push(() => {
try {
// 拿到 then 返回的结果
let x = onFulfilled(self.value);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
try {
let x = onRejected(self.error);
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
} else if (this.status === FULFILLED) {
return (bridgePromise = new MyPromise((resolve, reject) => {
try {
// 状态变为成功,会有相应的 self.value
let x = onFulfilled(self.value);
// 暂时可以理解为 resolve(x),后面具体实现中有拆解的过程
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}));
} else {
return (bridgePromise = new MyPromise((resolve, reject) => {
try {
// 状态变为失败,会有相应的 self.error
let x = onRejected(self.error);
// 暂时可以理解为 resolve(x),后面具体实现中有拆解的过程
resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}));
}
};
// catch 语法糖
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
// 传参为一个 MyPromise, 则直接返回它
// 传参为一个 thenable 对象,返回的 MyPromise 会跟随这个对象,采用它的最终状态作为自己的状态
// 其他情况,直接返回以该值为成功状态的 MyPromise对象
MyPromise.resolve = function (param) {
if (param instanceof MyPromise) return param;
return new MyPromise((resolve, reject) => {
if (param && param.then && typeof param.then === 'function') {
// param 状态变为成功会调用 resolve,将新 MyPromise 的状态变为成功,反之亦然
param.then(resolve, reject);
} else {
resolve(param);
}
});
};
// 传入的参数会作为一个 reason 原封不动地往下传
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
// 无论当前 MyPromise 是成功还是失败,调用finally之后都会执行 finally 中传入的函数,并且将值原封不动的往下传
MyPromise.prototype.finally = function (callback) {
return this.then(
(value) => {
return MyPromise.resolve(callback()).then(() => {
return value;
});
},
(error) => {
return MyPromise.resolve(callback()).then(() => {
return error;
});
}
);
};
// 使用node模块
let fs = require('fs');
let readFilePromise = (filename) => {
return new MyPromise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (!err) {
resolve(data);
} else {
reject(err);
}
});
});
};
readFilePromise('./001.txt')
.then((data) => {
console.log(data.toString());
return readFilePromise('./002.txt');
})
.then((data) => {
console.log(data.toString());
throw 'err';
})
.catch((err) => {
console.log('err:', err);
return "it's end";
})
.finally(() => {
console.log('finally');
})
.then((data) => {
console.log(data.toString());
});
new MyPromise((res1, rej) => {
res1(
new MyPromise((res2, rej) => {
res2(
new MyPromise((res3, rej) => {
res3('555');
}).then((data) => {
console.log(data + '666');
})
);
}).then((data) => {
console.log(data + '777');
})
);
}).then((data) => {
console.log(data + '888' + '\n');
});