参考文章

老话题:6个方法,检验你有没有正确使用设计模式
图说设计模式
结合开发中的实例,全面解读 JavaScript 设计模式
js中的设计模式

为啥学习设计模式

在系统的学习设计模式之后,我们需要达到3个层次:

  1. 能在白纸上画出所有的模式结构和时序图;
  2. 能用代码实现;如果模式的代码都没有实现过,是用不出来的;即所谓,看得懂,不会用;
  3. 灵活应用到工作中的项目中;

设计模式核心是面向对象的能力,而面向对象的很大特色就是:封装、继承、多态。绕不开的话题就是 提高代码复用性。
合理使用继承,在合适场景下多使用组合代替继承是良好的设计原则
学习面向对象最重要的是:深入理解面向对象的概念——封装、继承、多态的实现手段、目的。

在设计每个基类时候要仔细考虑它自身的特点,以及相关类的关系,才能合理地确定各个类之间的关系,是继承,还是组合,还是通过接口实现多态等等。

所有的模式说到底只是手段,实现类之间的交互才是目的。

继承是为了复用代码,但是——复用哪部分的代码?

  • 复用基类代码,但不是主要目的
  • 复用调用类方法的外部代码

设计原则

扩展阅读:对开发人员有用的定律、理论、原则和模式 https://github.com/nusr/hacker-laws-zh
单一职责原则

单一职责原则原则就是每个程序只负责做好一件事情,如果功能过于复杂就拆分开,每个部分保持独立

开放封闭原则

对扩展开放,对修改封闭

李氏置换原则

子类能够覆盖父类,父类能出现的地方,子类就可以出现

接口独立原则

保持接口的单一独立

依赖倒置原则

依赖倒置原则的含义是面向接口编程,依赖于抽象而不依赖于具体,使用方只关注接口而不关注具体类的实现

设计模式分类:
创建型

  • 工厂模式(工厂方法模式、抽象工厂模式、建造者模式)、单例模式

组合型

  • 适配器模式、装饰器模式、代理模式、外观模式

行为型

  • 观察者模式、状态模式

设计模式

工厂模式

jq的$ ,无需要new直接就可以得到jq的实例

  1. class jQuery {
  2. constructor(selector) {
  3. let slice = Array.prototype.slice
  4. let dom = slice.call(document.querySelectorAll(selector))
  5. let len = dom ? dom.length : 0
  6. for(let i = 0;i < len; i++) {
  7. this[i] = dom[i]
  8. }
  9. this.length = len
  10. this.selector = selector || ''
  11. }
  12. append(node) {
  13. }
  14. html(data) {
  15. }
  16. //等等API
  17. }
  18. window.$ = function (selector) {
  19. return new jQuery(selector)
  20. }

适配器

适配器模式的含义是旧接口格式和使用者不兼容,中间加一个适配器接口。生活当中随处可见符合适配器模式的例子,如:插头转换器,电脑接口转换器。

vue的computed

  1. <div id="example">
  2. <p>Original message: "{{ message }}"</p>
  3. <p>Computed reversed message: "{{ reversedMessage }}"</p>
  4. </div>
  5. var vm = new Vue({
  6. el: '#example',
  7. data: {
  8. message: 'Hello'
  9. },
  10. computed: {
  11. // 计算属性的 getter
  12. reversedMessage: function () {
  13. // `this` 指向 vm 实例
  14. return this.message.split('').reverse().join('')
  15. }
  16. }
  17. })

装饰器

装饰器模式,装饰我们可以理解为就是给一个东西装饰另外一些东西使其更好看,对应到前端中就是为对象添加新功能,并且不改变其原有的结构和功能,这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能

  1. class Hero {
  2. constructor(define = 10,attack = 20, blood = 20) {
  3. this.init(define,attack,blood)
  4. }
  5. init(define,attack,blood) {
  6. this.define = define;
  7. this.attack = attack;
  8. this.blood = blood;
  9. }
  10. toString() {
  11. return `防御力: ${this.define},攻击力:${this.attack},血量:${this.blood}`
  12. }
  13. }
  14. let houyi = new Hero();
  15. console.log(`当前状态 ===> ${houyi}`)
  16. //输出:当前状态 ===> 防御力:10,攻击力20,血量20
  17. function decorateCloth(target,key,descriptor) {
  18. const method= descriptor.value;
  19. let moreDef = 100;
  20. let ret;
  21. descriptor.value = (...args) => {
  22. args[0] += moreDef;
  23. ret = method.apply(target,args);
  24. return ret;
  25. }
  26. return descriptor;
  27. }
  28. class Hero {
  29. constructor(define = 10,attack = 20, blood = 20) {
  30. this.init(define,attack,blood)
  31. }
  32. @decorateCloth
  33. init(define,attack,blood) {
  34. this.define = define;
  35. this.attack = attack;
  36. this.blood = blood;
  37. }
  38. toString() {
  39. return `防御力: ${this.define},攻击力:${this.attack},血量:${this.blood}`
  40. }
  41. }
  42. let houyi = new Hero();
  43. console.log(`当前状态 ===> ${houyi}`)
  44. //输出:当前状态 ===> 防御力:110,攻击力20,血量20

代理模式

代理模式是使用者无权访问目标对象,中间加代理,通过代理做授权和控制

前端:事件代理

观察者

观察者模式就是只要你作为订阅者订阅了某个事件,当事件触发的时候,发布者就会通知你。这里可以类比我们去咖啡厅点咖啡,当我们点了咖啡之后,就可以去做别的事情,当咖啡完成时,服务员就会叫你来取,你到时候取走咖啡即可

网页事件绑定
nodejs的eventemitter

  1. const EventEmitter = require('events').EventEmitter
  2. const emitter1 = new EventEmitter();
  3. emitter1.on('some',()=> {
  4. //监听some事件
  5. console.log('some event is occured 1')
  6. })
  7. emitter1.on('some',()=> {
  8. //监听some事件
  9. console.log('some event is occured 2')
  10. })
  11. emitter.emit('some')

状态模式

状态总数(state)是有限的。•任一时刻,只处在一种状态之中。•某种条件下,会从一种状态转变(transition)到另一种状态。

如:ES6中的promise、(promise是ES6新增的一个特性,它是异步编程的一种解决方案,比传统的解决方案更加方便,可以使用.then()操作避免了回调嵌套带来的回调地狱式的写法)