javascript设计模式小结

最近终于看完买了很久的《Javascript设计模式与开发实践》,感觉还是受益匪浅的。 在这里做了一个简单的小结,将书上提到的设计模式尽量用自己的思路写出来,当然也是有所借鉴。书上的代码都是基于ES5的,本次小结会用到ES6来写。 感觉应该还是蛮多错漏的,欢迎指正。

单例模式(Singleton pattern)

确保一个类只有一个实例,并提供对该实例的全局访问。

function createSingleton(construct) {
  let storage = null;
  let handle = {
    construct: function(trapTarget, argumentList) {
      if (!storage) {
        storage = new trapTarget(argumentList[0]);
      }
      return storage;
    },
  };
  return new Proxy(construct, handle);
}
class Singleton {
  constructor(params) {
    this.val = params;
  }
  getVal() {
    console.log(this.val);
  }
  setVal(params) {
    this.val = params;
  }
}
// run
let ProxySingleton = createSingleton(Singleton);
let singletonObj_1 = new ProxySingleton(123);
let singletonObj_2 = new ProxySingleton(456);
console.log(singletonObj_1 === singletonObj_2); // true
复制代码

策略模式(Strategy pattern)

指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

const Strategy = {
  A(params) {
    console.log('Execute strategy A!', params);
  },
  B(params) {
    console.log('Execute strategy B!', params);
  },
  C(params) {
    console.log('Execute strategy C!', params);
  },
};
const StrategyExecute = function(params) {
  const { type, val } = params;
  return Strategy[type](val);
};
// run
StrategyExecute({ type: 'A', val: 123 });
StrategyExecute({ type: 'B', val: 456 });
StrategyExecute({ type: 'C', val: 789 });
复制代码

代理模式(Proxy pattern)

为其他对象提供一个代理以控制对这个对象的访问。

let game = {
  play() {
    console.log('Play!');
  },
};
let gameMachine = {
  token: 0,
  play: new Proxy(game.play, {
    apply(proxyTag, proxyThis, args) {
      if (proxyThis.token > 0) {
        proxyThis.token--;
        return Reflect.apply(proxyTag, proxyThis, args);
      } else {
        console.warn('No token!')
      }
    },
  })
};
// run
gameMachine.play(); // No token!
gameMachine.token = 1;
gameMachine.play(); //Play!
gameMachine.play(); // No token!
复制代码

迭代器模式(Iterator pattern)

提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

// 迭代器
function Iterator(params) {
  // 创建迭代器
  let iterator = params();
  // 开始迭代
  let stepResult = iterator.next();
  if (!stepResult.done) {
    // 定义下一步操作的方法,并执行
    (function step() {
      if (!stepResult.done) {
        if (typeof stepResult.value === 'function') {
          (async function() {
            let stepResultData = await stepResult.value();
            stepResult = iterator.next(stepResultData);
            step();
          })();
        } else {
          stepResult = iterator.next(stepResult.value);
          step();
        }
      }
    })();
  }
}
// 迭代步骤
function fetchData() {
  // ajax get some data
  return [
    {
      id: 1,
      name: 'test1',
    },
    {
      id: 2,
      name: 'test2',
    }
  ];
}
function resolveData(params) {
  // rebuild data
  return params.map(item => item.name);
}
let iteratorList = function *() {
  const apiData = yield fetchData();
  const list = yield resolveData(apiData);
  yield console.log(list);
};
// run
Iterator(iteratorList);
复制代码

观察者模式(Observer pattern)

发布-订阅模式(Publish–subscribe pattern)

在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新。

let Event = {
  _events: {},
  on(event, fn) {
    if (Array.isArray(event)) {
      event.forEach((e) => { this.on(e, fn) });
    } else {
      (this._events[event] || (this._events[event] = [])).push(fn);
    }
  },
  once(event, fn) {
    function on () {
      this.off(event, on);
      fn.apply(this, arguments);
    }
    on.fn = fn;
    this.on(event, on);
  },
  off(event, fn) {
    if (!arguments.length) {
      this._events = Object.create(null);
      return true;
    }
    if (Array.isArray(event)) {
      event.forEach((e) => { this.off(e, fn) });
      return true;
    }
    let eventFns = this._events[event];
    if (!eventFns) {
      return false;
    }
    if (!fn) {
      this._events[event] = null;
      return true;
    }
    if (fn) {
      let eventFn;
      let len = eventFns.length;
      while (len--) {
        eventFn = eventFns[len];
        if (eventFn === fn || eventFn.fn === fn) {
          eventFns.splice(len, 1);
          break;
        }
      }
      return true;
    }
  },
  emit(event) {
    let eventFns = this._events[event];
    if (eventFns) {
      let [ , ...rest ] = arguments;
      eventFns.forEach((fn) => {
        try {
          fn.apply(this, rest);
        } catch (err) {
          console.error(`Error: event handler for "${event}"`, err, this);
        }
      });
    }
  }
};
// run
Event.once('testEventBus', () => { console.log('Event had emitted.'); });
Event.emit('testEventBus'); // Event had emitted.
复制代码

命令模式(Command pattern)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。

class FanMachine {
  constructor() {
    this._commands = [];
  };
  execute(command, ...rest) {
    this._commands.push(command);
    command.execute(...rest);
  }
}
class Fan {
  static turnOn() {
    console.log('开启');
  };
  static turnOff() {
    console.log('关闭');
  };
  static windSpeed(params) {
    console.log('风速:', params);
  };
}
class setCommand {
  constructor(fn) {
    this.execute = fn;
  }
}
// run
let fanCommandTurnOn = new setCommand(Fan.turnOn);
let fanCommandTurnOff = new setCommand(Fan.turnOff);
let fanCommandWindSpeed = new setCommand(Fan.windSpeed);
let fanMachine = new FanMachine();
fanMachine.execute(fanCommandTurnOn); // 开启
fanMachine.execute(fanCommandWindSpeed, 1); //风速:1
fanMachine.execute(fanCommandWindSpeed, 2); //风速:2
fanMachine.execute(fanCommandWindSpeed, 3); //风速:3
fanMachine.execute(fanCommandTurnOff); // 关闭
复制代码

组合模式(Composite pattern)

把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。

class Composite {
  constructor() {
    this._commands = [];
  }
  add(command) {
    this._commands.push(command);
  }
  execute() {
    this._commands.forEach((command) => { command.execute(); })
  }
}
let commandA = {
  execute() {
    console.log('Execute A!');
  }
};
let commandB = {
  execute() {
    console.log('Execute B!');
  }
};
let commandC = {
  execute() {
    console.log('Execute C!');
  }
};
let commandD = new Composite();
commandD.add(commandB);
commandD.add(commandC);
let commandCenter = new Composite();
commandCenter.add(commandA);
commandCenter.add(commandD);
// run
commandCenter.execute();
// Execute A! 
// Execute B! 
// Execute C! 
复制代码

模板方法模式(Template method pattern)

模板方法模式准备一个抽象类,将部分逻辑以具体方法及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

class MakeDrink {
  constructor(name) {
    this._drinkName = name;
  }
  boilWater() {
    console.log('煮沸水');
  }
  pourInCup() {
    console.log('加原料');
  }
  brew() {
    console.log('冲泡');
  }
  addCondiments() {
    console.log('加调料');
  }
  init() {
    this.boilWater();
    this.pourInCup();
    this.brew();
    this.addCondiments();
  }
}
class MakeLemonTea extends MakeDrink {
  pourInCup() {
    console.log('加茶叶');
  }
  addCondiments() {
    console.log('加柠檬和糖');
  }
}
// run
let lemonTea = new MakeLemonTea('柠檬茶');
lemonTea.init();
// 煮沸水
// 加茶叶
// 冲泡
// 加柠檬和糖
复制代码

享元模式(Flyweight pattern)

通过共享以便有效的支持大量细颗粒对象。

// ...
复制代码

职责链模式(Chain-of-responsibility Pattern)

为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。

class Chain {
  constructor(fn) {
    this._fn = fn;
    this._next = null;
  }
  setNext(next) {
    return this._next = next;
  }
  pass(...args) {
    let ret = this._fn.apply(this, args);
    if (ret === 'pass') {
      return this._next && this._next.pass.apply(this._next, args);
    }
    return ret;
  }
  next(...args) {
    return this._next && this._next.pass.apply(this._next, args);
  }
}
let first = new Chain(function(params) {
  console.log('first: ', params);
  this.next(2);
});
let second = new Chain(function(params) {
  console.log('second: ', params);
  return 'pass';
});
let third = new Chain(function(params) {
  console.log('third: ', params);
  this.next(4);
});
let last = new Chain(function(params) {
  console.log('last: ', params);
  return false;
});
first.setNext(second).setNext(third).setNext(last);
// run
first.pass(1);
// first:  1
// second:  2
// third:  2
// last:  4
复制代码

中介者模式(Mediator pattern)

包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使它们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用,保证这些作用可以彼此独立的变化。

// ...
复制代码

装饰器(修饰)模式(Decorator Pattern)

向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。

class Decorator {
  constructor(params) {
    const {value, configurable, enumerable, writable} = params;
    this.value = value && typeof value === 'function' ? value : null;
    this.configurable = configurable || false;
    this.enumerable = enumerable || false;
    this.writable = writable || false;
  }
  static create(obj, prop, descriptor) {
    if (descriptor.value && typeof descriptor.value === 'function') {
      let origin = obj[prop];
      delete obj[prop];
      descriptor.value = descriptor.value(origin);
      Object.defineProperty(obj, prop, descriptor);
    }
  }
}

let decoratorTest = {
  name: 'test',
  init() {
    Decorator.create(this, 'func', new Decorator({
      value: function (fn) {
        return function () {
          // do something before...
          console.log('before!');
          fn.apply(this, arguments);
          // do something after...
          console.log('after!');
        }
      }
    }));
    console.log('init.');
  },
  func(...params) {
    console.log('decoratorTest: ', ...params);
  },
};
// run
decoratorTest.func(123);
decoratorTest.init();
decoratorTest.func(456);
// decoratorTest: 123
// init.
// begin!
// decoratorTest: 456
// end!
复制代码

状态模式(State pattern)

class State {
  constructor(params) {
    return State.create(this, params)
  }
  setState(state) {
    this.current = state;
    this.storage.push(state);
  }
  next() {
    const { name: currName, to: currTo } = this.main[this.current];
    if (currTo) {
      const nextName = this.main[currTo].name;
      const {beforeLeave} = this.hock[currName];
      const {beforeEnter, onMount} = this.hock[nextName];
      if (beforeLeave) { beforeLeave(); }
      this.setState(currTo);
      if (beforeEnter) { beforeEnter(); }
      if (onMount) { onMount(); }
    }
    return this.current;
  }
  previous() {
    return this.goBack(1);
  }
  goBack(num) {
    const storage = this.storage;
    const len = storage.length;
    if (len > 1) {
      if (len > num) {
        this.current = storage[len - num -1];
        this.storage = storage.slice(0, len - num);
      } else {
        this.current = storage[0];
        this.storage = [storage[0]];
      }
    }
    return this.current;
  }
  reset() {
    return this.goBack(this.storage.length - 1);
  }
  addHock(stateName, event, fn) {
    this.hock[stateName][event] = fn;
  }
  on(stateName, fn) {
    this.addHock(stateName, 'onMount', fn);
  }
  beforeEnter(stateName, fn) {
    this.addHock(stateName, 'beforeEnter', fn);
  }
  beforeLeave(stateName, fn) {
    this.addHock(stateName, 'beforeLeave', fn);
  }
  static create(_this, params) {
    const { initial, transitions, methods } = params;
    _this.current = initial;
    _this.storage = [initial];
    _this.main = {};
    _this.hock = {};
    transitions.forEach((e) => {
      const { name, from, to } = e;
      // main
      _this.main[from] = {};
      _this.main[from].name = name;
      _this.main[from].to = to;
      // hock
      _this.hock[name] = {};
      _this.hock[name].from = from;
      _this.hock[name].to = to;
      _this.hock[name].beforeEnter = methods[`beforeEnter${State._firstUpperCase(name)}`] || null;
      _this.hock[name].onMount = methods[`on${State._firstUpperCase(name)}`] || null;
      _this.hock[name].beforeLeave = methods[`beforeLeave${State._firstUpperCase(name)}`] || null;
      // event
      _this[name] = function () {
        const currStateName = _this.main[this.current].name;
        if (name !== currStateName) {
          const { beforeLeave } = _this.hock[currStateName];
          const { from, beforeEnter, onMount } = _this.hock[name];
          if (beforeLeave) { beforeLeave(); }
          _this.setState.call(_this, from);
          if (beforeEnter) { beforeEnter(); }
          if (onMount) { onMount(); }
          return from;
        } else {
          return this.current;
        }
      }
    });
    return _this;
  }
  static _firstUpperCase(str) {
    const [first, ...rest] = str;
    return `${first.toUpperCase()}${rest.join('')}`;
  }
}

const green = 'green';
const yellow = 'yellow';
const red = 'red';
let fsm = new State({
  initial: green,
  transitions: [
    {
      name: 'greenLight',
      from: green,
      to: yellow,
    },
    {
      name: 'yellowLight',
      from: yellow,
      to:red,
    },
    {
      name: 'redLight',
      from: red,
      to: green,
    },
  ],
  methods: {
    onGreenLight() { console.log('绿灯'); },
    onYellowLight() { console.log('黄灯'); },
    onRedLight() { console.log('红灯'); },
  }
});
// run
// 初始化状态为 green
fsm.next(); // 黄灯
fsm.next(); // 红灯
fsm.next(); // 绿灯
fsm.previous(); // return 'red'
fsm.greenLight(); // 绿灯
fsm.yellowLight(); // 黄灯
fsm.redLight(); // 红灯
复制代码

设配器模式(Adapter pattern)

将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。

// ...
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值