• javascript 进阶教程(02)


    JavaScript设计模式总结

    之前看过《JavaScript设计模式与开发实践》这本书,对书中的设计模式和一些相关案例也有了一定的了解,同时把这些设计模式的应用对应在在一些其他的项目中,进行了一些整理,如下仅供参考:

    设计模式目的

    • 设计模式是为了更好的代码重用性,可读性,可靠性,可维护性。

    • 透彻理解设计模式,写出令人赏心悦目的代码

    设计六大原则

    1)单一职责原则

    2)里氏替换原则

    3)依赖倒转原则

    4)接口隔离原则

    5)最少知识原则(迪米特法则)

    6)开放封闭原则

    设计模式分类

    总体来说设计模式分为三大类:

    创建型模式,共五种:工厂方法模式、抽象工厂模式单例模式、建造者模式、原型模式。

    结构型模式,共七种:适配器模式装饰器模式代理模式、外观模式、桥接模式、组合模式、享元模式。

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    其实还有两类:并发型模式和线程池模式。

    不过,对于前端来说,有的设计模式在平时工作中几乎用不到或者很少用到,接下来,我们了解下前端常见的设计模式

    JS中的设计模式

    常见设计模式:

    1、工厂模式

    常见的实例化对象模式,工厂模式就相当于创建实例对象的new,提供一个创建对象的接口

    1. // 某个需要创建的具体对象
    2. class Product {
    3. constructor (name) {
    4. this.name = name;
    5. }
    6. init () {}
    7. }
    8. // 工厂对象
    9. class Creator {
    10. create (name) {
    11. return new Product(name);
    12. }
    13. }
    14. const creator = new Creator();
    15. const p = creator.create(); // 通过工厂对象创建出来的具体对象

    比如:生产娃娃的工厂 属性:产地(东南亚/日本)、类型(硅胶/塑料)性别(男/女/未知)、

    ​ 方法(功能):说话(日本话、泰语),动作:360°自由旋转

    应用场景:JQuery中的$、Vue.component异步组件、React.createElement、Axios.get等

    2、单例模式

    保证一个类仅有一个实例,并提供一个访问它的全局访问点,一般登录、购物车等都是一个单例。

    1. // 单例对象
    2. class SingleObject {
    3. login () {}
    4. }
    5. // 访问方法
    6. SingleObject.getInstance = (function () {
    7. let instance;
    8. return function () {
    9. if (!instance) {
    10. instance = new SingleObject();
    11. }
    12. return instance;
    13. }
    14. })()
    15. const obj1 = SingleObject.getInstance();
    16. const obj2 = SingleObject.getInstance();
    17. console.log(obj1 === obj2); // true

    比如:公共厕所、女盆友、随身物品

    应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等

    3、适配器模式

    用来解决两个接口不兼容问题,由一个对象来包装不兼容的对象,比如参数转换,允许直接访问

    1. class Adapter {
    2. specificRequest () {
    3. return '德国标准插头';
    4. }
    5. }
    6. // 适配器对象,对原来不兼容对象进行包装处理
    7. class Target {
    8. constructor () {
    9. this.adapter = new Adapter();
    10. }
    11. request () {
    12. const info = this.adapter.specificRequest();
    13. console.log(`${info} - 转换器 - 中国标准插头`)
    14. }
    15. }
    16. const target = new Target();
    17. console.log(target.request()); // 德国标准插头 - 转换器 - 中国标准插头

    比如:脱胎换骨,睡了一觉凸然长出了翅膀

    应用场景:Vue的computed、旧的JSON格式转换成新的格式等

    4、装饰器模式

    在不改变对象自身的基础上,动态的给某个对象添加新的功能,同时又不改变其接口

    1. class Plane {
    2. fire () {
    3. console.log('发送普通子弹');
    4. }
    5. }
    6. // 装饰过的对象
    7. class Missile {
    8. constructor (plane) {
    9. this.plane = plane;
    10. }
    11. fire () {
    12. this.plane.fire();
    13. console.log('发射导弹');
    14. }
    15. }
    16. let plane = new Plane();
    17. plane = new Missile(plane);
    18. console.log(plane.fire()); // 依次打印 发送普通子弹 发射导弹

    比如:改造(比如 娃娃不能趴着)

    利用AOP给函数动态添加功能,即Function的after或者before

    1. Function.prototype.before = function (beforeFn) {
    2. const _self = this;
    3. return function () {
    4. beforeFn.apply(this, arguments);
    5. return _self.apply(this, arguments);
    6. }
    7. }
    8. Function.prototype.after = function (afterFn) {
    9. const _self = this;
    10. return function () {
    11. const ret = _self.apply(this, arguments);
    12. afterFn.apply(this, arguments);
    13. return ret;
    14. }
    15. }
    16. let func = function () {
    17. console.log('2');
    18. }
    19. func = func.before(function() {
    20. console.log('1');
    21. }).after(function() {
    22. console.log('3');
    23. })
    24. func();
    25. console.log(func()); // 依次打印 1 2 3

    应用场景:redux中间件thunk 、ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等

    5、代理模式

    为其他对象提供一种代理,便以控制对这个对象的访问,不能直接访问目标对象

    1. class Flower {}
    2. // 源对象
    3. class Jack {
    4. constructor (target) {
    5. this.target = target;
    6. }
    7. sendFlower (target) {
    8. const flower = new Flower();
    9. this.target.receiveFlower(flower)
    10. }
    11. }
    12. // 目标对象
    13. class Rose {
    14. receiveFlower (flower) {
    15. console.log('收到花: ' + flower)
    16. }
    17. }
    18. // 代理对象
    19. class ProxyObj {
    20. constructor () {
    21. this.target = new Rose();
    22. }
    23. receiveFlower (flower) {
    24. this.sendFlower(flower)
    25. }
    26. sendFlower (flower) {
    27. this.target.receiveFlower(flower)
    28. }
    29. }
    30. const proxyObj = new ProxyObj();
    31. const jack = new Jack(proxyObj);
    32. jack.sendFlower(proxyObj); // 收到花:[object Object]


    比如:我是他老子

    应用场景:ES6 Proxy、Vuex中对于getters访问、图片预加载等

    6、外观模式

    为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易,不符合单一职责原则和开放封闭原则

    1. class A {
    2. eat () {}
    3. }
    4. class B {
    5. eat () {}
    6. }
    7. class C {
    8. eat () {
    9. const a = new A();
    10. const b = new B();
    11. a.eat();
    12. b.eat();
    13. }
    14. }
    15. // 跨浏览器事件侦听器
    16. function addEvent(el, type, fn) {
    17. if (window.addEventListener) {
    18. el.addEventListener(type, fn, false);
    19. } else if (window.attachEvent) {
    20. el.attachEvent('on' + type, fn);
    21. } else {
    22. el['on' + type] = fn;
    23. }
    24. }

    比如:暗恋

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

    7、观察者模式

    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知

    1. class Subject {
    2. constructor () {
    3. this.state = 0;
    4. this.observers = [];
    5. }
    6. getState () {
    7. return this.state;
    8. }
    9. setState (state) {
    10. this.state = state;
    11. this.notify();
    12. }
    13. notify () {
    14. this.observers.forEach(observer => {
    15. observer.update();
    16. })
    17. }
    18. attach (observer) {
    19. this.observers.push(observer);
    20. }
    21. }
    22. class Observer {
    23. constructor (name, subject) {
    24. this.name = name;
    25. this.subject = subject;
    26. this.subject.attach(this);
    27. }
    28. update () {
    29. console.log(`${this.name} update, state: ${this.subject.getState()}`);
    30. }
    31. }
    32. let sub = new Subject();
    33. let observer1 = new Observer('o1', sub);
    34. let observer2 = new Observer('o2', sub);
    35. sub.setState(1);

    观察者模式 与 发布/订阅模式区别: 本质上的区别是调度的地方不同

    虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。

    ---观察者模式:目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。 比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的界面的观察者B,B就把自己注册到A里,当A触发天气变化,就调度B的更新方法,并带上自己的上下文。

    ---发布/订阅模式:订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。 比如有个界面是实时显示天气,它就订阅天气事件(注册到调度中心,包括处理程序),当天气变化时(定时获取数据),就作为发布者发布天气信息到调度中心,调度中心就调度订阅者的天气处理程序。

    比如:观察对方是否喜欢自己、警察和小偷

    应用场景:JS事件、JS Promise、JQuery.$CallBack、Vue watch、NodeJS自定义事件,文件流等

    8、迭代器模式

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

    可分为:内部迭代器和外部迭代器

    内部迭代器: 内部已经定义好迭代规则,外部只需要调用一次即可。

    1. const each = (args, fn) => {
    2. for (let i = 0, len = args.length; i < len; i++) {
    3. const value = fn(args[i], i, args);
    4. if (value === false) break;
    5. }
    6. }

    应用场景: JQuery.each方法 、Array.prototype.map

    外部迭代器:必须显示的请求迭代下一个元素。

    1. // 迭代器
    2. class Iterator {
    3. constructor (list) {
    4. this.list = list;
    5. this.index = 0;
    6. }
    7. next () {
    8. if (this.hasNext()) {
    9. return this.list[this.index++]
    10. }
    11. return null;
    12. }
    13. hasNext () {
    14. if (this.index === this.list.length) {
    15. return false;
    16. }
    17. return true;
    18. }
    19. }
    20. const arr = [1, 2, 3, 4, 5, 6];
    21. const ite = new Iterator();
    22. while(ite.hasNext()) {
    23. console.log(ite.next()); // 依次打印 1 2 3 4 5 6
    24. }

    应用场景:JS IteratorJS Generator、遍历链表中的next、二叉树遍历中的left/right

    9、状态模式

    关键是区分事物内部的状态,事物内部状态往往会带来事物的行为改变,即允许对象在内部状态发生改变时改变它的行为

    1. // 红灯
    2. class RedLight {
    3. constructor (state) {
    4. this.state = state;
    5. }
    6. light () {
    7. console.log('turn to red light');
    8. this.state.setState(this.state.greenLight)
    9. }
    10. }
    11. // 绿灯
    12. class greenLight {
    13. constructor (state) {
    14. this.state = state;
    15. }
    16. light () {
    17. console.log('turn to green light');
    18. this.state.setState(this.state.yellowLight)
    19. }
    20. }
    21. // 黄灯
    22. class yellowLight {
    23. constructor (state) {
    24. this.state = state;
    25. }
    26. light () {
    27. console.log('turn to yellow light');
    28. this.state.setState(this.state.redLight)
    29. }
    30. }
    31. class State {
    32. constructor () {
    33. this.redLight = new RedLight(this)
    34. this.greenLight = new greenLight(this)
    35. this.yellowLight = new yellowLight(this)
    36. this.setState(this.redLight) // 初始化为红灯
    37. }
    38. setState (state) {
    39. this.currState = state;
    40. }
    41. }
    42. const state = new State();
    43. state.currState.light() // turn to red light
    44. setInterval(() => {
    45. state.currState.light() // 每隔3秒依次打印红灯、绿灯、黄灯
    46. }, 3000)

    比如:女生的心情

    应用场景:灯泡状态、红绿灯切换等

    其他设计模式:

    10、命令模式 11、组合模式 12、享元模式 13、策略模式 14、职责链模式 15、模板方法模式 16、中介者模式 17、备忘录模式 18、访问者模式 19、解释器模式 20、桥接模式

  • 相关阅读:
    燃冬之yum、vim和你
    java计算机毕业设计智能推荐二手车交易网站源码+mysql数据库+系统+部署+lw文档
    vscode使用git
    数据分区设计(0)-前言
    分布式存储系统之Ceph集群访问接口启用
    C语言日记 33 构造函数
    软件测试知识库+1,5款顶级自动化测试工具推荐和使用分析
    【JVM】五种对象引用
    2核4G服务器 如何设计编码一款扛得住高并发高吞吐量的商品秒杀系统
    Java面试题总结(二):Java多线程
  • 原文地址:https://blog.csdn.net/weixin_45311714/article/details/127650508