面试之-javascript设计模式

558 阅读3分钟

一, 参考文献: <<Javascipt 设计模式与开发实践>> -- 曾探

二, 设计模式的目的

  • 设计模式是为了更好的代码重用性, 可读性,可靠性, 可维护性
  • 学会理解了设计模式, 就会发现生活中无处不在

三, 设计模式分类

  • 创建型: 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
  • 结构型: 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型: 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 以下我们将学习常见的设计模式:

四, 常见设计模式

1. 单例模式

  • 定义: 保证一个类仅有一个实例, 并提供一个访问他的全局访问点
  • 比如有些对象我们只需要一个, 比如线程池, 全局缓存, 登录, 购物车等, 这种我们适合使用单例模式
    class SingleObj {
      login() {}
    }
    SingleObj.getInstance = (function() {
      let instance;
      return function() {
        if (!instance) {
          // 如果实例没有被创建, 则创建新的实例
          instance = new SingleObj();
        }
        return instance;
      };
    })();
    let a = SingleObj.getInstance();
    let b = SingleObj.getInstance();
    // console.log("a", a == b);
  • 应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等

2. 策略模式

  • 定义: 定义一系列的算法, 并把他们一个个封装起来, 并且使他们可以相互替换
    let strategy = {
      A: function(price) {
        return price * 2;
      },
      B: function(price) {
        return price * 2;
      },
      C: function(price) {
        return price * 2;
      }
    };
    function calc(price, type) {
      return strategy[type](price);
    }
    calc(100, "A");

  • 应用场景: 表单验证等

3. 代理模式

  • 定义: 为一个对象提供一个代用品或占位符, 以便控制对他的访问
    // 案例: jack 通过代理对象送花给rose
    class Flower {}
    class Jack {
      constructor(target) {
        this.target = target;
      }
      sendFlower(target) {
        // jack送花 rose收到花
        const flower = new Flower(); // 创建花
        this.target.receiveFlower(flower); // 目标对象收到花
      }
    }
    // 目标对象
    class Rose {
      receiveFlower(flower) {
        console.log("收到花", flower);
      }
    }
    class Proxy {
      constructor() {
        this.target = new Rose(); // 目标对象为rose
      }
      sendFlower(flower) {
        // 代理对象送花
        this.receiveFlower(flower);
      }
      // 目标对象收到花
      receiveFlower(flower) {
        this.target.receiveFlower(flower);
      }
    }

    let proxyPerson = new Proxy(); // 代理对象
    let jack = new Jack(proxyPerson); // 杰克
    jack.sendFlower(proxyPerson); // 杰克送花
  • 应用场景: ES6 proxy, Vuex中对于getters访问、图片预加载等

4. 迭代器模式

  • 定义: 按一个方法顺序访问一个聚合对象中的每一个元素, 而又无需暴露该对象的内部显示

内部迭代器

  • 内部定义好迭代规则, 外部只需要调用一次
    const each = (args, fn) => {
      for (let i = 0; i < args.length; i++) {
        const value = fn(args[i], i, args);
        if (!value) break; // 跳出循环
      }
    };
    each([1, 2, 3], function(item, index) {
      console.log(item, index);
    });

外部迭代器

  • 必须显示的请求迭代下一个元素。
    class Iterator {
      constructor(list) {
        this.list = list;
        this.index = 0;
      }
      next() {
        if (this.hasNext()) {
          return this.list[this.index++];
        }
        return null;
      }
      hasNext() {
        if (this.list.length === this.index) {
          return false;
        }
        return true;
      }
    }
    let ite = new Iterator([1, 2, 3, 4]);
    ite.next();
  • 应用场景: JS Iterator、JS Generator

5. 工厂模式

  • 定义: 常见的实例化对象模式
   // 具体的对象
    class Product {
      constructor(name) {
        this.name = name;
      }
    }
    // 工厂类
    class Creator {
      create(name) {
        return new Product(name);
      }
    }

    let creators = new Creator();
    creators.create("jojo");
  • 应用场景:JQuery中的$、Vue.component异步组件、React.createElement等

6. 发布订阅者模式

7. 装饰者模式

  • 定义: 在不改变对象本身的基础上, 在程序运行期间动态的添加方法
  • 简单版
    class Plane {
      fire() {
        console.log("开火");
      }
    }
    class Mixin {
      constructor(obj) {
        this.obj = obj;
      }
      attack() {
        this.obj.fire();
        console.log("开炮");
      }
    }
    let plane = new Plane();
    let mixin = new Mixin(plane);
    mixin.attack();
  • 利用AOP给函数动态添加功能
    Function.prototype.before = function(fn) {
      let self = this; // 保存原函数引用
      return function() {
        fn.apply(this, arguments); // 新函数执行
        return self.apply(this, arguments); // 原函数执行
      };
    };
    Function.prototype.after = function(fn) {
      let self = this;
      return function() {
        let ret = self.apply(this, arguments);
        fn.apply(this, arguments);
        return ret;
      };
    };

    function f1() {
      console.log(1);
    }
    let f = f1
      .before(function() {
        console.log(2);
      })
      .after(function() {
        console.log(3);
      });
    f();
  • 应用场景:ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等

8.适配器模式

  • 定义: 用来解决两个接口不兼容的问题, 由一个对象包裹不兼容的对象, 比如参数转换, 允许直接访问
    class Adapter {
      specify() {
        return "德国标准插头";
      }
    }
    class Target {
      constructor(target) {
        this.target = new Adapter();
      }
      requset() {
        let tips = this.target.specify();
        console.log(`${tips} - 转换器 - 中国标准插头`);
      }
    }
    let target1 = new Target();
    target1.requset();

9. 中介者模式

  • 定义: 通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,
  • 当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。
    var mediator = (function() {
      var colorSelect = document.getElementById("colorSelect");
      var memorySelect = document.getElementById("memorySelect");
      var numSelect = document.getElementById("numSelect");
      return {
        changed: function(obj) {
          switch (obj) {
            case colorSelect:
              //TODO
              break;
            case memorySelect:
              //TODO
              break;
            case numSelect:
              //TODO
              break;
          }
        }
      };
    })();
    colorSelect.onchange = function() {
      mediator.changed(this);
    };
    memorySelect.onchange = function() {
      mediator.changed(this);
    };
    numSelect.onchange = function() {
      mediator.changed(this);
    };

10. 外观模式

  • 为一组复杂的子系统接口提供一个更高级的统一接口, 通过这个接口更容易访问子系统接口, 不符合开放封闭原则
    class A {
      eat() {}
    }

    class B {
      eat() {}
    }
    class C {
      eat() {
        const a = new A();
        const b = new B();
        a.eat();
        b.eat();
      }
    }

    // 跨浏览器事件监听
    function addEvent(el, type, fn) {
      if (window.addEventListener) {
        el.addEventListener(type, fn, false);
      } else if (window.attachEvent) {
        el.attachEvent('on' +type, fn);
      } else {
        el["on" + type] = fn;
      }
    }

  • 应用场景:JS事件不同浏览器兼容处理、同一方法可以传入不同参数兼容处理等

今天真是让人头凸且充实的一天啊~~~