1 单例模式
-
定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点, 比如 webpack打包的模块, 在多个文件多次引用某个文件的情况下只打包一次该文件.
-
核心
确保只有一个实例,并提供全局访问
-
实现
假设要设置一个管理员,多次调用也仅设置一次,我们可以使用闭包缓存一个内部变量来实现这个单例
// 构造函数
function SetManager(name) {
this.manager = name;
}
SetManager.prototype.getName = function() {
console.log(this.manager);
};
//函数内部使用了单例模式,只生成一个manager对象
var SingletonSetManager = (function() {
var manager = null;
return function(name) {
if (!manager) {
manager = new SetManager(name);
}
return manager;
}
})();
SingletonSetManager('a').getName(); // a
SingletonSetManager('b').getName(); // a
SingletonSetManager('c').getName(); // a
2 策略模式
1. 定义
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
2. 核心
将算法的使用和算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成:
第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
第二个部分是环境类Context,Context接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明Context 中要维持对某个策略对象的引用
3. 实现
策略模式可以用于组合一系列算法,也可用于组合一系列业务规则
假设需要通过成绩等级来计算学生的最终得分,每个成绩等级有对应的加权值。我们可以利用对象字面量的形式直接定义这个组策略
// 加权映射关系
var levelMap = {
S: 10,
A: 8,
B: 6,
C: 4
};
// 组策略
var scoreLevel = {
basicScore: 80,
S: function() {
return this.basicScore + levelMap['S'];
},
A: function() {
return this.basicScore + levelMap['A'];
},
B: function() {
return this.basicScore + levelMap['B'];
},
C: function() {
return this.basicScore + levelMap['C'];
}
}
// 调用
function getScore(level) {
return scoreLevel[level] ? scoreLevel[level]() : 0;
}
console.log(
getScore('S'),
getScore('A'),
getScore('B'),
getScore('C'),
getScore('D')
); // 90 88 86 84 0
3 代理模式
1. 定义
为一个对象提供一个代用品或占位符,以便控制对它的访问
2. 核心
当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。
替身对象对请求做出一些处理之后, 再把请求转交给本体对象
代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情
3. 实现
保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子
// 主体,发送消息
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
4 发布-订阅模式
1. 定义
也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知
2. 核心
取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。
与传统的发布-订阅模式实现方式(将订阅者自身当成引用传入发布者)不同,在JS中通常使用注册回调函数的形式来订阅
3. 实现
JS中的事件就是经典的发布-订阅模式的实现
// 订阅
document.body.addEventListener('click', function() {
console.log('click1');
}, false);
document.body.addEventListener('click', function() {
console.log('click2');
}, false);
// 发布
document.body.click(); // click1 click2
// 观察者
var observer = {
// 订阅集合
subscribes: {},
// 订阅
subscribe: function(type, fn) {
if (!this.subscribes[type]) {
this.subscribes[type] = [];
}
// 收集订阅者的处理
typeof fn === 'function' && this.subscribes[type].push(fn);
},
// 发布 可能会携带一些信息发布出去
publish: function() {
var type = [].shift.call(arguments),
fns = this.subscribes[type];
// 不存在的订阅类型,以及订阅时未传入处理回调的
if (!fns || !fns.length) {
return;
}
// 挨个处理调用
for (var i = 0; i < fns.length; ++i) {
fns[i].apply(this, arguments);
}
},
// 删除订阅
remove: function(type, fn) {
// 删除全部
if (typeof type === 'undefined') {
this.subscribes = [];
return;
}
var fns = this.subscribes[type];
// 不存在的订阅类型,以及订阅时未传入处理回调的
if (!fns || !fns.length) {
return;
}
if (typeof fn === 'undefined') {
fns.length = 0;
return;
}
// 挨个处理删除
for (var i = 0; i < fns.length; ++i) {
if (fns[i] === fn) {
fns.splice(i, 1);
}
}
}
};
// 订阅岗位列表
function jobListForA(jobs) {
console.log('A', jobs);
}
function jobListForB(jobs) {
console.log('B', jobs);
}
// A订阅了笔试成绩
observer.subscribe('job', jobListForA);
// B订阅了笔试成绩
observer.subscribe('job', jobListForB);
// A订阅了笔试成绩
observer.subscribe('examinationA', function(score) {
console.log(score);
});
// B订阅了笔试成绩
observer.subscribe('examinationB', function(score) {
console.log(score);
});
// A订阅了面试结果
observer.subscribe('interviewA', function(result) {
console.log(result);
});
observer.publish('examinationA', 100); // 100
observer.publish('examinationB', 80); // 80
observer.publish('interviewA', '备用'); // 备用
observer.publish('job', ['前端', '后端', '测试']); // 输出A和B的岗位
// B取消订阅了笔试成绩
observer.remove('examinationB');
// A都取消订阅了岗位
observer.remove('job', jobListForA);
observer.publish('examinationB', 80); // 没有可匹配的订阅,无输出
observer.publish('job', ['前端', '后端', '测试']); // 输出B的岗位
5 命令模式
1. 定义
用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系
命令(command)指的是一个执行某些特定事情的指令
// 自增
function IncrementCommand() {
// 当前值this.val = 0;
// 命令栈this.stack = [];
// 栈指针位置this.stackPosition = -1;
};
// 定义一个命令
IncrementCommand.prototype = {
constructor: IncrementCommand,
// 执行
execute: function() {
this._clearRedo();
// 定义执行的处理var command = function() {
this.val += 2;
}.bind(this);
// 执行并缓存起来 command();
this.stack.push(command);
this.stackPosition++;
this.getValue();
},
canUndo: function() {
return this.stackPosition >= 0;
},
canRedo: function() {
return this.stackPosition < this.stack.length - 1;
},
// 撤销
undo: function() {
if (!this.canUndo()) {
return;
}
this.stackPosition--;
// 命令的撤销,与执行的处理相反var command = function() {
this.val -= 2;
}.bind(this);
// 撤销后不需要缓存 command();
this.getValue();
},
// 重做
redo: function() {
if (!this.canRedo()) {
return;
}
// 执行栈顶的命令this.stack[++this.stackPosition]();
this.getValue();
},
// 在执行时,已经撤销的部分不能再重做
_clearRedo: function() {
this.stack = this.stack.slice(0, this.stackPosition + 1);
},
// 获取当前值
getValue: function() {
console.log(this.val);
}
};
var incrementCommand = new IncrementCommand();
// 模拟事件触发,执行命令var eventTrigger = {
// 某个事件的处理中,直接调用命令的处理方法
increment: function() {
incrementCommand.execute();
},
incrementUndo: function() {
incrementCommand.undo();
},
incrementRedo: function() {
incrementCommand.redo();
}
};
eventTrigger['increment'](); // 2
eventTrigger['increment'](); // 4
eventTrigger['incrementUndo'](); // 2
eventTrigger['increment'](); // 4
eventTrigger['incrementUndo'](); // 2
eventTrigger['incrementUndo'](); // 0
eventTrigger['incrementUndo'](); // 无输出
eventTrigger['incrementRedo'](); // 2
eventTrigger['incrementRedo'](); // 4
eventTrigger['incrementRedo'](); // 无输出
eventTrigger['increment'](); // 6
6 模板方法模式
1. 定义
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。
2. 核心
在抽象父类中封装子类的算法框架,它的 init方法可作为一个算法的模板,指导子类以何种顺序去执行哪些方法。
由父类分离出公共部分,要求子类重写某些父类的(易变化的)抽象方法
3. 实现
模板方法模式一般的实现方式为继承
以运动作为例子,运动有比较通用的一些处理,这部分可以抽离开来,在父类中实现。具体某项运动的特殊性则有自类来重写实现。
最终子类直接调用父类的模板函数来执行
// 体育运动
function Sport() {
}
Sport.prototype = {
constructor: Sport,
// 模板,按顺序执行
init: function() {
this.stretch();
this.jog();
this.deepBreath();
this.start();
var free = this.end();
// 运动后还有空的话,就拉伸一下
if (free !== false) {
this.stretch();
}
},
// 拉伸
stretch: function() {
console.log('拉伸');
},
// 慢跑
jog: function() {
console.log('慢跑');
},
// 深呼吸
deepBreath: function() {
console.log('深呼吸');
},
// 开始运动
start: function() {
throw new Error('子类必须重写此方法');
},
// 结束运动
end: function() {
console.log('运动结束');
}
};
// 篮球
function Basketball() {
}
Basketball.prototype = new Sport();
// 重写相关的方法
Basketball.prototype.start = function() {
console.log('先投上几个三分');
};
Basketball.prototype.end = function() {
console.log('运动结束了,有事先走一步');
return false;
};
// 马拉松
function Marathon() {
}
Marathon.prototype = new Sport();
var basketball = new Basketball();
var marathon = new Marathon();
// 子类调用,最终会按照父类定义的顺序执行
basketball.init();
marathon.init();
7 职责链模式
1. 定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链 传递该请求,直到有一个对象处理它为止
2. 核心
请求发送者只需要知道链中的第一个节点,弱化发送者和一组接收者之间的强联系,可以便捷地在职责链中增加或删除一个节点,同样地,指定谁是第一个节点也很便捷
3. 实现
以展示不同类型的变量为例,设置一条职责链,可以免去多重if条件分支
// 定义链的某一项
function ChainItem(fn) {
this.fn = fn;
this.next = null;
}
ChainItem.prototype = {
constructor: ChainItem,
// 设置下一项
setNext: function(next) {
this.next = next;
return next;
},
// 开始执行
start: function() {
this.fn.apply(this, arguments);
},
// 转到链的下一项执行
toNext: function() {
if (this.next) {
this.start.apply(this.next, arguments);
} else {
console.log('无匹配的执行项目');
}
}
};
// 展示数字
function showNumber(num) {
if (typeof num === 'number') {
console.log('number', num);
} else {
// 转移到下一项
this.toNext(num);
}
}
// 展示字符串
function showString(str) {
if (typeof str === 'string') {
console.log('string', str);
} else {
this.toNext(str);
}
}
// 展示对象
function showObject(obj) {
if (typeof obj === 'object') {
console.log('object', obj);
} else {
this.toNext(obj);
}
}
var chainNumber = new ChainItem(showNumber);
var chainString = new ChainItem(showString);
var chainObject = new ChainItem(showObject);
// 设置链条
chainObject.setNext(chainNumber).setNext(chainString);
chainString.start('12'); // string 12
chainNumber.start({}); // 无匹配的执行项目
chainObject.start({}); // object {}
chainObject.start(123); // number 123
参考资料:
https://www.cnblogs.com/imwtr/p/9451129.html#o6