创建型设计模式
单例模式:保证一个类只有一个实例,并提供一个全局访问点。
Vue中的axios实例(我们对axios进行请求拦截和响应拦截,多次调用封装好的axios但是仅设置一次,封装好的axios导出就是一个单例)、全局态管理 store
var createMask = function(){
var mask;
return function(){
return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
}
}()
工厂模式:定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
function CreatePerson(name,age,sex) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function(){
return this.name;
};
return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
结构型设计模式
装饰者模式:在不改变原对象的基础上,通过对其进行包装拓展(添加属性和方法)使原有对象更加满足用户的复杂需求。
let decorate = function(input, fn){
let input = document.getElementById("input");
if(typeof input.onclick === "function"){
var oldClickFn = input.onclick;
input.onclick = function(){
oldClickFn();
fn();
}
}
else{
input.onclick = fn();
}
}
代理模式:由于一个对象不能直接应用另一个对象,所以需要通过代理对象其搭配中介作用
行为型设计模式
观察者模式:定义了一种依赖关系, 解决了主体对象与观察者之间功能的耦合 vue中的computed 事件绑定 watch监听
var Observer = (function(){
var _message = {};
return {
//注册接口
regist: function(type, fn){
//如果消息此消息不存在则应该创建一个消息类型
if(typeof _message[type] === 'undefined'){
//将动作推入到该消息对一个的动作执行队列中
_message[type] = [fn];
}else{
_message[type].push(fn);
}
},
//发布信息接口
fire: function(type, args){
//如果该消息没有被注册,则返回
if(!_message[type]){
return;
}
//定义消息信息
var events={
type: type,
args: args || {}
}
for(let i=0; _message[type].length; i++){
_message[type][i].call(this, events);
}
},
//移除信息接口
remove: function(){
//如果消息队列存在
if(_message[type] instanceof Array){
//从最后一个消息动作开始遍历
for(let i=_message[type].length-1; i>=0; i--){
//如果存在该动作则移除相应动作
_message[type][i] === fn && _message[type].splice(i, 1);
}
}
}
}
})();
//订阅一条消息
Observer.regist('test', function(e){
console.log(e.type, e.args.msg);
});
//发布上条消息
Observer.fire('test', {msg: '发布成功啦~'});
适配器模式:
在程序里适配器模式也经常用来适配 2 个接口, 比如你现在正在用一个自定义的 js 库. 里面有个根据 id 获取节点的方法 $id(). 有天你觉得 jquery 里的 $实现得更酷, 但你又不想让你的工程师去学习新的库和语法. 那一个适配器就能让你完成这件事情.
$id = function( id ){
return jQuery( '#' + id )[0];
}
代理模式
正向代理:
反向代理(nginx)
反向代理是基于服务端的,不对外暴露真实的服务器;
class Store extends Factory {
constructor() {
super();
}
sall(): string {
this.total--;
return "领克03"
}
afterSale(): void {
console.log("售后服务!");
}
}
class Custoemr {
shop(car: string): void {
console.log(`买了一辆${car}`);
}
}
// 实例化出一个做代理的对象
const lingke = new Store();
// 实例化一个 使用代理对象的对象
const zs = new Custoemr();
zs.shop(lingke.sall());
桥接模式
桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
class Speed { // 运动模块
constructor(x, y) {
this.x = x
this.y = y
}
run() { console.log(`运动起来 ${this.x} + ${this.y}`) }
}
class Color { // 着色模块
constructor(cl) {
this.color = cl
}
draw() { console.log(`绘制颜色 ${this.color}`) }
}
class Speak {
constructor(wd) {
this.word = wd
}
say() { console.log(`说话 ${this.word}`) }
}
class Ball { // 创建球类,可以着色和运动
constructor(x, y, cl) {
this.speed = new Speed(x, y)
this.color = new Color(cl)
}
init() {
this.speed.run()
this.color.draw()
}
}
class Man { // 人类,可以运动和说话
constructor(x, y, wd) {
this.speed = new Speed(x, y)
this.speak = new Speak(wd)
}
init() {
this.speed.run()
this.speak.say()
}
}
const man = new Man(1, 2, 'hehe?')
man.init() // 运动起来 1 + 2 说话 hehe?
外观模式
为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易
function addEventToDOM(dom,type,fn){
if(dom.addEventListener){
dom.addEventListener(type,fn,false);
}else if(dom.attachEvent){
dom.attachEvent('on'+type,fn);
}else{
dom[on+'type'] = fn ;
}
}
访问者模式
访问者模式的思想就是在不改变操作对象的同时,为它添加新的操作方法,以实现对操作对象的访问。我们知道,call 和 apply 的作用就是更改函数执行时的作用域,这正是访问者模式的精髓。通过 call、apply 这两种方式我们就可以让某个对象在其它作用域中运行。
// 访问者模式:数组方法封装
var Visitor = (function() {
return {
splice: function() {
var args = Array.prototype.splice.call(arguments, 1);
return Array.prototype.splice.apply(arguments[0], args);
},
push: function() {
var len = arguments[0].length || 0;
var args = this.splice(arguments, 1);
arguments[0].length = len + arguments.length - 1;
return Array.prototype.push.apply(arguments[0], args);
},
pop: function() {
return Array.prototype.pop.apply(arguments[0]);
}
}
})();
var a = new Object();
Visitor.push(a,1,2,3,4);//4
Visitor.push(a,4,5,6);//7
Visitor.pop(a);//6
Visitor.splice(a,2)//[3,4,4,5];
策略模式
1. Context :封装上下文,根据需要调用需要的策略,屏蔽外界对策略的直接调用,只对外提供一个接口,根据需要调用对应的策略;
2. Strategy :策略,含有具体的算法,其方法的外观相同,因此可以互相代替;
3. StrategyMap :所有策略的合集,供封装上下文调用;
export const StrategyMap = {
// Strategy 1: 将文件大小(bit)转化为 KB
bitToKB: val => {
const num = Number(val)
return isNaN(num) ? val : (num / 1024).toFixed(0) + 'KB'
},
// Strategy 2: 将文件大小(bit)转化为 MB
bitToMB: val => {
const num = Number(val)
return isNaN(num) ? val : (num / 1024 / 1024).toFixed(1) + 'MB'
}
}
// Context: 生成el表单 formatter
const strategyContext = function (type, rowKey) {
return function (row, column, cellValue, index) {
return StrategyMap[type](row[rowKey])
}
}
模板方式模式
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。
var Beverage = function(){};
Beverage.prototype.boilWater = function(){
console.log( '把水煮沸' );
};
Beverage.prototype.brew = function(){
throw new Error( '子类必须重写brew方法' );
};
Beverage.prototype.pourInCup = function(){
throw new Error( '子类必须重写pourInCup方法' );
};
Beverage.prototype.addCondiments = function(){
throw new Error( '子类必须重写addCondiments方法' );
};
Beverage.prototype.customerWantsCondiments = function(){
return true; // 默认需要调料
};
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
if ( this.customerWantsCondiments() ){ // 如果挂钩返回true,则需要调料
this.addCondiments();
}
};
var CoffeeWithHook = function(){};
CoffeeWithHook.prototype = new Beverage();
CoffeeWithHook.prototype.brew = function(){
console.log( '用沸水冲泡咖啡' );
};
CoffeeWithHook.prototype.pourInCup = function(){
console.log( '把咖啡倒进杯子' );
};
CoffeeWithHook.prototype.addCondiments = function(){
console.log( '加糖和牛奶' );
};
CoffeeWithHook.prototype.customerWantsCondiments = function(){
return window.confirm( '请问需要调料吗?' );
};
var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();
var Beverage = function( param ){
var boilWater = function(){
console.log( '把水煮沸' );
};
var brew = param.brew || function(){
throw new Error( '必须传递brew方法' );
};
var pourInCup = param.pourInCup || function(){
throw new Error( '必须传递pourInCup方法' );
};
var addCondiments = param.addCondiments || function(){
throw new Error( '必须传递addCondiments方法' );
};
var F = function(){};
F.prototype.init = function(){
boilWater();
brew();
pourInCup();
addCondiments();
};
return F;
};
var Coffee = Beverage({
brew: function(){
console.log( '用沸水冲泡咖啡' );
},
pourInCup: function(){
console.log( '把咖啡倒进杯子' );
},
addCondiments: function(){
console.log( '加糖和牛奶' );
}
});
var Tea = Beverage({
brew: function(){
console.log( '用沸水浸泡茶叶' );
},
pourInCup: function(){
console.log( '把茶倒进杯子' );
},
addCondiments: function(){
console.log( '加柠檬' );
}
});
var coffee = new Coffee();
coffee.init();
var tea = new Tea();
tea.init();
中介者模式
let mediator = function() {
let relative = {};
return {
add( name, relation ){
relative[name] = relation;
},
remove( name ) {
delete relative[name];
},
payNewYear( name ) {
relative[name] & console.log(`${relative[name]}, 新年好!恭喜发财红包拿来!`);
}
}
}();
mediator.add('狗蛋','二表舅');
mediator.add('铁柱','五大爷');
mediator.payNewYear('狗蛋');
mediator.payNewYear('铁柱');
mediator.remove('铁柱');
迭代器模式
迭代器模式是指提供一种方法顺序访问一个对象中的各个元素,又不需要暴露该对象的内部。
//内部迭代器
var each = function(arr, callback) {
for(let i=0,l = arr.length;i < l;i++) {
callback.call(arr[i], i, arr[i])
}
};
each([1,2,3,4],function( i, n) {
console.log( i, n)
})
//each 函数属于内部迭代器,函数内部已经定好了迭代规则,调用非常方便,但无法同时迭代2个数组
//外部迭代器
let iterator = function(obj) {
let current = 0 //
let next = function() {
current += 1
}
let isDone = function() {
return current >= obj.length
}
let getcurrent = function(){
return obj[current]
}
return {
next: next,
isDone: isDone,
getCurrItem: getcurrent
}
}
let compare = function(iterator1, iterator2) {
while( !iterator1.isDone() && !iterator2.isDone()){
if ( iterator1.getcurrent() !== iterator2.getcurrent() ){
throw new Error ( 'iterator1 和 iterator2 不相等' );
}
iterator1.next();
iterator2.next();
}
}
var iterator1 = Iterator( [ 1, 2, 3 ] );
var iterator2 = Iterator( [ 1, 2, 3 ] );
compare( iterator1, iterator2 ); // 输出:iterator1 和 iterator2 相等
//外部迭代器相对比较复杂,但是可以实现更多的需求,也能满足更多变的需求,两种迭代器没有优劣之分,按场景来判断究竟使用哪个迭代器。
组合模式
// 创建一个宏命令
var MacroCommand = function(){
return {
// 宏命令的子命令列表
commandsList: [],
// 添加命令到子命令列表
add: function( command ){
this.commandsList.push( command );
},
// 依次执行子命令列表里面的命令
execute: function(){
for ( var i = 0, command; command = this.commandsList[ i++ ]; ){
command.execute();
}
}
}
};
<!--打开空调命令-->
var openAcCommand = {
execute: function(){
console.log( '打开空调' );
}
};
<!--打开电视和音响-->
var openTvCommand = {
execute: function(){
console.log( '打开电视' );
}
};
var openSoundCommand = {
execute: function(){
console.log( '打开音响' );
}
};
//创建一个宏命令
var macroCommand1 = MacroCommand();
//把打开电视装进这个宏命令里
macroCommand1.add(openTvCommand)
//把打开音响装进这个宏命令里
macroCommand1.add(openSoundCommand)
<!--关门、打开电脑和打登录QQ的命令-->
var closeDoorCommand = {
execute: function(){
console.log( '关门' );
}
};
var openPcCommand = {
execute: function(){
console.log( '开电脑' );
}
};
var openQQCommand = {
execute: function(){
console.log( '登录QQ' );
}
};
//创建一个宏命令
var macroCommand2 = MacroCommand();
//把关门命令装进这个宏命令里
macroCommand2.add( closeDoorCommand );
//把开电脑命令装进这个宏命令里
macroCommand2.add( openPcCommand );
//把登录QQ命令装进这个宏命令里
macroCommand2.add( openQQCommand );
<!--把各宏命令装进一个超级命令中去-->
var macroCommand = MacroCommand();
macroCommand.add( openAcCommand );
macroCommand.add( macroCommand1 );
macroCommand.add( macroCommand2 );
状态模式
class StoreContext{
constructor(){
this.state = new LowState(this);
}
setState(state){
this.state = state;
}
getState(){
return this.state;
}
//分数合计
add(score){
this.state.addScore(this,score);
}
}
class Score{
//抽象方法,检查当前状态
checkState(context){}
//分数合计-状态之间的业务逻辑
addScore(context,score){
console.log("当前分数:"+this.score+",当前状态:"+this.stateName);
console.log("加上"+score+"之后,");
this.score += score;
this.checkState(context);
console.log("分数为:"+this.score+",状态为:"+context.getState().stateName);
}
}
class LowState extends Score{
constructor(state){
super(state);
this.stateName = "不及格";
this.score = state.score||0;
}
checkState(context){
if(this.score>=90 ){
//大于90分时,状态为【优秀】
context.setState(new HighState(this))
}else if(this.score>=60){
//大于60分时,状态为【中等】
context.setState(new MiddleState(this))
}
}
}
class MiddleState extends Score{
constructor(state){
super(state);
this.stateName = "中等";
this.score = state.score;
}
checkState(context){
if(this.score<60 ){
//小于60分时,状态为【不及格】
context.setState(new LowState(this))
}else if(this.score>=90){
context.setState(new HighState(this))
}
}
}
class HighState extends Score{
constructor(state){
super(state);
this.stateName = "优秀";
this.score = state.score;
}
checkState(context){
if(this.score<60 ){
context.setState(new LowState(this))
}else if(this.score<90){
context.setState(new MiddleState(this))
}
}
}
class Customer{
static main(){
let context = new StoreContext();
context.add(30);
context.add(40);
context.add(-60);
context.add(100);
}
}
Customer.main();
享元模式
享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
var Model = function( sex ){
this.sex = sex;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);
};
var maleModel = new Model( 'male' ),
femaleModel = new Model( 'female' );
for ( var i = 1; i <= 50; i++ ){
maleModel.underwear = 'underwear' + i;
maleModel.takePhoto();
};
for ( var j = 1; j <= 50; j++ ){
femaleModel.underwear = 'underwear' + j;
femaleModel.takePhoto();
};
备忘录模式
// 备忘类
class Memento {
constructor(content) {
this.content = content;
}
getContent() {
return this.content;
}
}
// 备忘列表
class CarTaker {
constructor() {
this.list = [];
}
add(memento) {
this.list.push(memento);
}
get(index) {
return this.list[index];
}
getList() {
return this.list
}
}
// 编辑器
class Editor {
constructor() {
this.content = null;
}
setContent(content) {
this.content = content;
}
getContent() {
return this.content;
}
saveContentToMemento() {
return new Memento(this.content);
}
getConentFromMemento(memento) {
this.content = memento.getContent();
}
}
// 测试代码
let editor = new Editor()
let careTaker = new CarTaker()
editor.setContent('111')
editor.setContent('222')
careTaker.add(editor.saveContentToMemento()) // 将当前222内容备份
editor.setContent('333')
careTaker.add(editor.saveContentToMemento()) // 将当前333内容备份
editor.setContent('444')
console.log(editor.getContent())
editor.getConentFromMemento(careTaker.get(1)) // 撤销
console.log(editor.getContent())
editor.getConentFromMemento(careTaker.get(0)) // 撤销
console.log(editor.getContent())
var order500 = function (orderType,pay,stock) {
if(orderType === 1 && pay === true){
console.log("500元定金预购,得到100元优惠券");
}else{
return "nextSuccessor";
}
}
var order200 = function (orderType,pay,stock) {
if(orderType === 2 && pay === true){
console.log("200元定金预购,得到50元优惠券");
}else{
return "nextSuccessor";
}
}
var orderNormal = function (orderType,pay,stock) {
if(stock > 0){
console.log("普通购买无优惠券");
}else{
console.log("手机库存不足");
}
}
var Chain = function (fn) {
this.fn = fn;
this.successor = null;
}
Chain.prototype.setSuccessor = function (fn) {
return this.successor = fn;
}
Chain.prototype.passRequest = function () {
var ret = this.fn.apply(this, arguments);
if(ret === "nextSuccessor"){
return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
return ret;
}
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);
chainOrder500.setSuccessor(chainOrder200);
chainOrder200.setSuccessor(chainOrderNormal);
chainOrder500.passRequest(1,false,0);