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]);
    })
    

    其他

    等用到了再补充吧

  • 相关阅读:
    C++ 多态性之虚函数&抽象类&纯虚函数
    转帖:iOS UIWindow & UIWindowLevel
    转帖:iOS UINavigationController 对象
    iOS 数据持久化 NSUserDefault
    ----百度地图api----
    ----改写superheros的json以及上传到github----
    ----XMLHttpRequestAPI简单介绍----
    ----event flow----
    ----Arrow functions----
    ----constructor 与 object----
  • 原文地址:https://www.cnblogs.com/everlose/p/12501032.html
Copyright © 2011-2022 走看看