单例模式
单例模式需确保只有一个实例且可以全局访问。
实现单例模式
let __instance = (function() {
let instance;
return newInstance => {
if (newInstance) instance = newInstance;
return instance;
};
})();
class Singleton {
constructor(name) {
this.name = name;
if (!__instance()) {
__instance(this);
}
return __instance();
}
}
测试:
const s1 = new Singleton("s1");
const s2 = new Singleton("s2");
assert.strictEqual(s1, s2);
assert.strictEqual(s1.name, s2.name);
实践
单例模式需要满足只有一个实例且可全局访问即可,可以使用 JavaScript 的闭包来实现。接下来以弹窗为例:
function createPopup(content) {
const div = document.createElement("div");
div.innerHTML = content;
return div;
}
将单例模式和创建弹窗代码解耦:
function createSingleton (fn) {
let result
return function () {
return result || result = fn.apply(this, arguments)
}
}
const createSingletonPopup = createSingleton(createPopup)
发布订阅模式
事件发布订阅模式可以帮助完成更松的解耦。
EventEmiiter 的简单实现
class EventEmitter {
constructor() {
this.listener = new Map();
}
on(type, fn) {
const subs = this.listener.get(type);
if (!subs) {
this.listener.set(type, [fn]);
} else {
this.listener.set(type, [].concat(subs, fn));
}
}
emit(...args) {
const type = args[0];
for (let listener of this.listener.get(type)) {
listener(...args.slice(1));
}
}
}
测试代码:
const em = new EventEmitter();
em.on("click", counter => {
assert.equal(counter, 1);
});
em.emit("click", 1);
代理模式
代理对象和本体对象具有一致的接口,对使用者友好。代理模式的种类有很多种,在 JavaScript 中常见的是:虚拟代理和缓存代理。
虚拟代理实现图片预加载
const myImg = (() => {
const img = document.appendChild("img");
document.body.appendChild(img);
return {
setSrc: src => (img.src = src)
};
})();
const proxyImage = (() => {
const img = new Image();
img.onload = function() {
myImg.setSrc(this.src);
};
return {
setSrc: src => {
myImg.setSrc("loading.png");
img.src = src;
}
};
})();
proxyImage.setSrc("loaded.jpg");
缓存代理实现乘积计算
const multiply = function(...args) {
return args.reduce((accu, curr) => (accu *= curr), 1);
};
const proxyMultiply = (() => {
const cache = {};
return (...args) => {
let tag = args.join(",");
if (cache[tag]) {
return cache[tag];
}
return (cache[tag] = multiply.apply(null, args));
};
})();
策略模式
顾名思义,根据不同的参数(或配置)有不同的策略(函数)。
表单验证
以表单验证为例,不同的字段应有不同的验证方法,即不同的策略。
class Checker {
constructor(check, message) {
this.check = check;
this.message = message;
}
}
class Validator {
constructor(config) {
this.config = config;
this.messages = [];
}
validate(data) {
for (let [k, v] of Object.entries(data)) {
const type = this.config.get(k);
const checker = Validator[type];
const result = checker.check(v);
if (!result) {
this.messages.push(checker.message(v));
}
}
return this;
}
isError() {
return this.messages.length > 0;
}
}
测试代码:
const data = {
name: "startegy",
age: 0
};
const config = new Map([["name", "isNotEmpty"], ["age", "isGreaterThan"]]);
Validator.isNotEmpty = new Strategy.Checker(
val => val.length > 0,
val => `The ${val} is empty`
);
Validator.isGreaterThan = new Strategy.Checker(
number => number > 20,
number => `The number ${number} is less than 20`
);
assert.equal(new Validator(config).validate(data).isError(), true);
迭代器模式
能访问到聚合对象的顺序和元素。
ES6 的 iterator 接口
const data = {
data: [1, 2, 3, 4, 5, 6],
[Symbol.iterator]() {
const len = this.data.length;
let index = 0;
return {
next: () => {
return index < len
? { value: this.data[index++], done: false }
: { value: undefined, done: true };
},
rewind: () => (index = 0)
};
}
};
单例模式
单例模式需确保只有一个实例且可以全局访问。
实现单例模式
测试:
实践
单例模式需要满足只有一个实例且可全局访问即可,可以使用 JavaScript 的闭包来实现。接下来以弹窗为例:
将单例模式和创建弹窗代码解耦:
发布订阅模式
事件发布订阅模式可以帮助完成更松的解耦。
EventEmiiter 的简单实现
测试代码:
代理模式
代理对象和本体对象具有一致的接口,对使用者友好。代理模式的种类有很多种,在
JavaScript中常见的是:虚拟代理和缓存代理。虚拟代理实现图片预加载
缓存代理实现乘积计算
策略模式
顾名思义,根据不同的参数(或配置)有不同的策略(函数)。
表单验证
以表单验证为例,不同的字段应有不同的验证方法,即不同的策略。
测试代码:
迭代器模式
能访问到聚合对象的顺序和元素。
ES6 的
iterator接口