zoukankan      html  css  js  c++  java
  • JS 设计模式-工作常用的

    单例模式

    // 单例构造函数
    function CreateSingleton (name) {
        this.name = name;
        this.getName();
    };
    
    // 获取实例的名字
    CreateSingleton.prototype.getName = function() {
        console.log(this.name)
    };
    // 单例对象
    var Singleton = (function(){
        var instance;
        return function (name) {
            if(!instance) {
                instance = new CreateSingleton(name);
            }
            return instance;
        }
    })();
    
    // 创建实例对象1
    var a = new Singleton('a');
    // 创建实例对象2
    var b = new Singleton('b');
    
    console.log(a===b);
    

    工厂模式

    //User类
    class User {
      //构造器
      constructor(opt) {
        this.name = opt.name;
        this.viewPage = opt.viewPage;
      }
    
      //静态方法
      static getInstance(role) {
        switch (role) {
          case 'superAdmin':
            return new User({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] });
            break;
          case 'admin':
            return new User({ name: '管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据'] });
            break;
          case 'user':
            return new User({ name: '普通用户', viewPage: ['首页', '通讯录', '发现页'] });
            break;
          default:
            throw new Error('参数错误, 可选参数:superAdmin、admin、user')
        }
      }
    }
    
    //调用
    let superAdmin = User.getInstance('superAdmin');
    let admin = User.getInstance('admin');
    let normalUser = User.getInstance('user');
    

    发布/订阅者模式

    手写 EventEmitter

    class EventEmitter {
      constructor() {
        this.events = {};
      }
      on(eventName, func) {
        if (!this.events[eventName]) {
          this.events[eventName] = [];
        }
        this.events[eventName].push(func);
      }
      emit(eventName, ...params) {
        const events = this.events[eventName];
        if (events) {
          events.forEach(event => {
            event.apply(this, params)
          })
        }
      }
      once(eventName, func) {
        const cb = () => {
          func();
          this.off(eventName, func);
        }
        this.on(eventName, cb);
      }
      off(eventName, func) {
        if (this.events[eventName]) {
          if (!func) {
            this.events[eventName] = []
          } else {
            this.events[eventName].splice(this.events[eventName].indexOf(func), 1)
          }
        }
      }
    }
    

    装饰器模式

    写 react 的时候经常会遇到,本质上它就是个包装函数,主要用于多个不同类之间共享或者扩展一些方法或者行为。

    function isAnimal(target) {
        target.isAnimal = true;
      	return target;
    }
    
    @isAnimal
    class Cat {
        ...
    }
    
    console.log(Cat.isAnimal);    // true
    

    值得一提的是,装饰器居然还在 tc39 的 stage-2 阶段。

    所以要用的话需要加 babel-plugin-transform-decorators-legacy 插件

    babel --plugins transform-decorators-legacy es6.js > es5.js
    

    策略模式

    常用于避免写很多 if 条件语句的情况,打个比方算绩效奖金的函数

    function calcSalary(salary, score) {
      if (score === '4') {
        return salary * 6
      } else if (score === '3.75') {
        return salary * 4
      } else if (score === '3.5+') {
        return salary * 3
      } else if (score === '3.5') {
        return salary * 2
      } else if (score === '3.5-') {
        return salary * 1
      } else if (score === '3.25') {
        return 0;
      }
    }
    

    这是阿里系的公司可能的计算年终奖的规则,那么某天想增加一个等级叫 3.75+,那又得往上面加 if 判断。就不希望这么做。所以可以使用策略模式优化

    var defaultMap = {
      '4': function (salary) { return salary * 6 },
      '3.75': function (salary) { return salary * 4 },
      '3.5+': function (salary) { return salary * 3 },
      '3.5': function (salary) { return salary * 2 },
      '3.5-': function (salary) { return salary * 1 },
      '3.25': function (salary) { return 0 },
    };
    
    function calcSalary(salary, score, extendMap) {
      let map = Object.assign({}, defaultMap, extendMap);
      return map[score](salary);
    }
    
    calcSalary(10000, '3.75+', {
      '3.75+': function (salary) { return salary * 5 }
    })
    

    用策略模式也可以简单封装表单,举个例子,你可以在代码中去循环这个结构输出 DOM,当然这样做又会遇到其他问题另说。

    var formStruct = [
      {
        id: 1,
        type: 'input',
        rule: /d+/,
        placeholder: 'input something'
      },
      {
        id: 2,
        type: 'select',
        require: true,
        options: []
      },
      ........
    ]
    

    适配器模式

    摘录自前端小时-JavasScript设计模式浅析

    适配器模式就相当于一个转换接口,大家想想我们手机充电器通常是二岔口的,但是电源只有三岔口的。这时候就需要一个适配器把三岔口的转换成二岔口的。

    它的作用其实就是解决两个软件实体间的接口不兼容问题,使用之后就可以一起工作了。

    var googleMap = {
      show: function () {
        console.log('googleMap show!');
      },
    };
    var baiduMap = {
      show: function () {
        console.log('baiduMap show!');
      },
    };
    
    var renderMap = function (map) {
      if (map.show instanceof Function) {
        map.show();
      }
    };
    renderMap(googleMap);
    renderMap(baiduMap);
    
    

    上面这段程序能够运行是因为百度地图和谷歌地图用的同一种show方法,但是我们在不知道对方使用的函数接口的时候,我们就不能这样用了(可能百度是使用了display方法来显示)。下面的baiduMapAdapter就是我们使用的适配器。

    var googleMap = {
      show: function () {
        console.log('googleMap show!');
      },
    };
    var baiduMap = {
      display: function () {
        console.log('baiduMap show!');
      },
    };
    
    var renderMap = function (map) {
      if (map.show instanceof Function) {
        map.show();
      }
    };
    
    var baiduMapAdapter = {
      show: function () {
        return baiduMap.display();
      },
    };
    renderMap(googleMap);
    renderMap(baiduMapAdapter);
    
    

    代理模式

    很久以前前端去绑定事件时,还很流行一个术语叫“事件委托”,就借用这个思想。

    点击 li 标签显示它的内容,但是直接对 li 遍历添加事件对 JS 性能不太好,故而可以把事件监听设置在父元素 ul 上。

    <ul id="ul">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
    </ul>
    <script>
      let ul = document.querySelector('#ul')
      ul.addEventListener('click', (event) => {
        console.log(event.target);
        if (event.target.tagName === 'li') {
          
        }
      })
    </script>
    

    迭代器模式

    迭代器就是提供一种可以顺序访问的模式,forEach 就是一种常用的迭代器

    var forEach = function (arr, callback) {
      for (var i = 0, l = arr.length; i < l; i++) {
        callback.call(arr[i], i, arr[i])
      }
    }
    
    // callback 顺序访问数组里的内容
    forEach([1, 2, 3], function (item, n) {
      console.log([item, n]);
    })
    

    其他

    等用到了再补充吧

  • 相关阅读:
    leetcode 33. Search in Rotated Sorted Array
    leetcode 32. Longest Valid Parentheses
    leetcode 28. Implement strStr()
    leetcode 27. Remove Element
    leetcode 26. Remove Duplicates from Sorted Array
    leetcode 24. Swap Nodes in Pairs
    leetcode 22. Generate Parentheses
    树莓派的频率管理和热控制
    sql执行insert插入一条记录同时获取刚插入的id
    全程直播个人博客重构过程,采用springboot+dubbo+jpa技术栈。
  • 原文地址:https://www.cnblogs.com/everlose/p/12501032.html
Copyright © 2011-2022 走看看