• 用js理解常用设计模式


    目录

    原则

    创建型

    单例模式

    工厂模式

    js闭包:函数工厂

    结构型

    代理模式

    装饰器模式

    行为型

    职责链模式

    观察者/发布订阅模式:收集依赖 -> 触发通知 -> 取出依赖执行

    手写

    promise

    跨组件事件通信/中央事件总线

    Vue3:mitt,Vue2:eventbus

    响应式

    Vue3:proxy,Vue2:definePropert

    webpack plugin

    cancelToken实例

    原则

    • S – Single Responsibility Principle 单一职责原则
      • 一个程序只做好一件事
      • 如果功能过于复杂就拆分开,每个部分保持独立
      • 例如:Promise每个then中的逻辑只做好一件事
    • O – OpenClosed Principle 开放/封闭原则
      • 对扩展开放,对修改封闭
      • 增加需求时,扩展新代码,而非修改已有代码
      • 例如:Promise如果新增需求,扩展then
    • L – Liskov Substitution Principle 里氏替换原则
      • 子类能覆盖父类
      • 父类能出现的地方子类就能出现
    • I – Interface Segregation Principle 接口隔离原则
      • 保持接口的单一独立
      • 类似单一职责原则,这里更关注接口
    • D – Dependency Inversion Principle 依赖倒转原则
      • 面向接口编程,依赖于抽象而不依赖于具体
      • 使用方只关注接口而不关注具体类的实现
    1. //checkType('165226226326','mobile')
    2. //result:false
    3. let checkType=function(str, type) {
    4. switch (type) {
    5. case 'email':
    6. return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str)
    7. case 'mobile':
    8. return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
    9. case 'tel':
    10. return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
    11. default:
    12. return true;
    13. }
    14. }

    想添加其他规则就得在函数里面增加 case 。违反了开放-封闭原则(对扩展开放,对修改关闭)

    给 API 增加一个扩展的接口:

    1. let checkType=(function(){
    2. let rules={
    3. email(str){
    4. return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
    5. },
    6. mobile(str){
    7. return /^1[3|4|5|7|8][0-9]{9}$/.test(str);
    8. }
    9. };
    10. //暴露接口
    11. return {
    12. //校验
    13. check(str, type){
    14. return rules[type]?rules[type](str):false;
    15. },
    16. //添加规则
    17. addRule(type,fn){
    18. rules[type]=fn;
    19. }
    20. }
    21. })();
    22. //调用方式
    23. //使用mobile校验规则
    24. console.log(checkType.check('188170239','mobile'));
    25. //添加金额校验规则
    26. checkType.addRule('money',function (str) {
    27. return /^[0-9]+(.[0-9]{2})?$/.test(str)
    28. });
    29. //使用金额校验规则
    30. console.log(checkType.check('18.36','money'));

    创建型

    单例模式

    一个类只有一个实例,并提供一个访问它的全局访问点。

    1. class LoginForm {
    2. constructor() {
    3. this.state = 'hide'
    4. }
    5. show() {
    6. if (this.state === 'show') {
    7. alert('已经显示')
    8. return
    9. }
    10. this.state = 'show'
    11. console.log('登录框显示成功')
    12. }
    13. hide() {
    14. if (this.state === 'hide') {
    15. alert('已经隐藏')
    16. return
    17. }
    18. this.state = 'hide'
    19. console.log('登录框隐藏成功')
    20. }
    21. }
    22. LoginForm.getInstance = (function () {
    23. let instance
    24. return function () {
    25. if (!instance) {
    26. instance = new LoginForm()
    27. }
    28. return instance
    29. }
    30. })()
    31. let obj1 = LoginForm.getInstance()
    32. obj1.show()
    33. let obj2 = LoginForm.getInstance()
    34. obj2.hide()
    35. console.log(obj1 === obj2)

    优点:

    • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
    • 单例模式设置全局访问点,可以优化和共享资源的访问。
    • 只会实例化一次。简化了代码的调试和维护

    缺点:

    • 单例模式一般没有接口,扩展困难
    • 有可能导致模块间的强耦合 从而不利于单元测试。

    应用:登录框

    工厂模式

    工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。

    该模式使一个类的实例化延迟到了子类

    而子类可以重写接口方法以便创建的时候指定自己的对象类型

    1. class Product1 {
    2. product() {
    3. console.log("生产一线");
    4. }
    5. }
    6. class Product2 {
    7. product() {
    8. console.log("生产二线");
    9. }
    10. }
    11. class Factory {
    12. constructor() {
    13. this.Product1 = Product1;
    14. this.Product2 = Product2;
    15. }
    16. create(name, callBack) {
    17. const product = new this[name]();
    18. product.product();
    19. return callBack("susess");
    20. }
    21. }
    22. let p = new Factory();
    23. p.create("Product1", (res) => {
    24. console.log(res);
    25. });

     不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中

    1. class Car {
    2. constructor(name, color) {
    3. this.name = name;
    4. this.color = color;
    5. }
    6. }
    7. class Factory {
    8. static create(type) {
    9. switch (type) {
    10. case "car":
    11. return new Car("汽车", "白色");
    12. break;
    13. case "bicycle":
    14. return new Car("自行车", "黑色");
    15. break;
    16. default:
    17. console.log("没有该类型");
    18. }
    19. }
    20. }
    21. let p1 = Factory.create("car");
    22. let p2 = Factory.create("bicycle");
    23. console.log(p1, p1 instanceof Car); // {name: '汽车', color: '白色'} true
    24. console.log(p2, p2 instanceof Car); // {name: '自行车', color: '黑色'} true

    js闭包:函数工厂

    1. function makeAdder(x) {
    2. return function (y) {
    3. return x + y;
    4. };
    5. }
    6. var add5 = makeAdder(5);// fun(y){5+y}
    7. var add10 = makeAdder(10);// fun(y){10+y}
    8. console.log(add5(2)); // 7
    9. console.log(add10(2)); // 12

    优点:

    • 工厂职责单一化易于维护
    • 有利于消除对象间的耦合,提供更大的灵活性

    缺点:添加新产品时,需要编写新的具体产品类,一定程度上增加了系统的复杂度

    结构型

    • 装饰者模式: 扩展功能,原有功能不变且可直接使用
    • 代理模式: 显示原有功能,但是经过限制之后的

    代理模式

    是为一个对象提供一个代用品或占位符,以便控制对它的访问

    应用:

    • ES6 的 proxy 
    • HTML元 素事件代理
    1. 1."ul"标签添加点击事件
    2. 2. 当点击某"li"标签时,该标签内容拼接"."符号。如:某"li"标签被点击时,该标签内容为".."
    3. 注意:
    4. 1. 必须使用DOM0级标准事件(onclick)
    5. target表示当前触发事件的元素
    6. currentTarget是绑定处理函数的元素
    7. 只有当事件处理函数绑定在自身的时候,target才会和currentTarget一样
      • <li>.li>
      • <li>.li>
      • <li>.li>
  • <script type="text/javascript">
  • document.querySelector('ul').onclick=event=>{
  • event.target.innerText+='.'
  • }
  • script>
  • 装饰器模式

    动态地给某个对象添加一些额外的职责,是一种实现继承的替代方案

    1. class Cellphone {
    2. create() {
    3. console.log('生成一个手机')
    4. }
    5. }
    6. class Decorator {
    7. constructor(cellphone) {
    8. this.cellphone = cellphone
    9. }
    10. create() {
    11. this.cellphone.create()
    12. this.createShell(cellphone)
    13. }
    14. createShell() {
    15. console.log('生成手机壳')
    16. }
    17. }
    18. // 测试代码
    19. let cellphone = new Cellphone()
    20. cellphone.create()
    21. console.log('------------')
    22. let dec = new Decorator(cellphone)
    23. dec.create()

    优点:

    缺点:

    应用:

    行为型

    职责链模式

    使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求直到有一个对象处理它为止

    1. // 请假审批,需要组长审批、经理审批、总监审批
    2. class Action {
    3. constructor(name) {
    4. this.name = name
    5. this.nextAction = null
    6. }
    7. setNextAction(action) {
    8. this.nextAction = action
    9. }
    10. handle() {
    11. console.log( `${this.name} 审批`)
    12. if (this.nextAction != null) {
    13. this.nextAction.handle()
    14. }
    15. }
    16. }
    17. let a1 = new Action("组长")
    18. let a2 = new Action("经理")
    19. let a3 = new Action("总监")
    20. a1.setNextAction(a2)
    21. a2.setNextAction(a3)
    22. a1.handle()

    优点:

    缺点:

    应用:

    观察者/发布订阅模式:收集依赖 -> 触发通知 -> 取出依赖执行

    手写

    promise

    跨组件事件通信/中央事件总线

    Vue3:mitt,Vue2:eventbus

    响应式

    Vue3:proxy,Vue2:definePropert

    webpack plugin

    cancelToken实例

  • 相关阅读:
    React面试题总结(一)
    国外网站国内镜像
    第十四章 集合(集合框架体系、List)
    面试了个 985 毕业的同学,回答“性能调优”题时表情令我毕生难忘
    Linux - 第2节 - Linux环境基础开发工具使用
    nrf52832升级原理
    短视频矩阵系统源代码开发|技术源代码部署/OEM贴牌搭建
    Leetcode双指针刷题(一)
    数据结构-图的基本概念
    荧光标记多肽FITC/AMC/FAM/Rhodamine/TAMRA/Cy3/Cy5/Cy7-Peptide
  • 原文地址:https://blog.csdn.net/qq_28838891/article/details/133140789