JavaScript 中的设计模式

news2024/11/24 3:58:22

目录

1. 单例模式

2. 策略模式

3. 代理模式

4. 装饰者模式

5. 组合模式

6. 工厂模式

7. 访问者模式

8. 发布订阅模式

9. 观察者模式

10. 参考链接


设计模式(Design Pattern)是从许多优秀的软件系统中,总结出的成功的、能够实现可维护的、复用的设计方案,使用这些方案 可以避免开发者做一些重复性工作

1. 单例模式

一个类只能构造出唯一实例

应用案例:弹框

class Single {
  constructor(name) {
    this.name = name;
  }

  static getInstance(name) {
    // 静态方法
    if (!this.instance) {
      // 关键代码 this指向的是Single这个构造函数
      this.instance = new Single(name);
    }
    return this.instance;
  }
}

const single111 = Single.getInstance('name111');
const single222 = Single.getInstance('name222');

console.log(single111 === single222); // true

2. 策略模式

根据不同参数命中不同的策略

应用案例:表单验证

// 策略对象
const strategies = {
  // 验证是否为空
  isNoEmpty(value, errorMsg) {
    if (value.trim() === '') {
      return errorMsg;
    }
  },
  // 验证最小长度
  minLength(value, length, errorMsg) {
    if (value.trim().length < length) {
      return errorMsg;
    }
  },
  // 验证最大长度
  maxLength(value, length, errorMsg) {
    if (value.length > length) {
      return errorMsg;
    }
  },
  // 验证手机号
  isMobile(value, errorMsg) {
    if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
      return errorMsg;
    }
  },
};

// 验证类
class Validator {
  constructor() {
    // 存储要验证的方法
    this.cache = [];
    // 存储最终的验证结果
    this.errList = [];
  }

  /**
   * 添加 表单项 验证规则
   * @param value 表单项值
   * @param rules 验证规则数组
   */
  add(value, rules) {
    for (let i = 0, rule; (rule = rules[i++]);) {
      /*
       * 将形如 strategy: 'minLength:6', 的分割为数组
       * 获取校验规则、数量
       */
      const strategyAry = rule.strategy.split(':');
      // 校验失败后的提示信息
      const { errorMsg } = rule;
      this.cache.push(() => {
        // 获取校验规则,形如 minLength
        const strategy = strategyAry.shift();
        // 新增表单值,形如 L
        strategyAry.unshift(value);
        // 新增报错信息
        strategyAry.push(errorMsg);
        // 执行策略对象中的不同验证规则
        const error = strategies[strategy](...strategyAry);
        // 如果没通过校验,就不断填充报错信息列表s
        if (error) {
          this.errList.push(error);
        }
      });
    }
  }

  start() {
    for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]);) {
      validatorFunc();
    }
    return this.errList;
  }
}

const validataFunc = function (formData) {
  // 实例化验证类
  const validator = new Validator();
  // 配置 用户名 验证规则
  validator.add(formData.userName, [
    {
      strategy: 'isNoEmpty',
      errorMsg: '用户名不可为空',
    },
    {
      strategy: 'minLength:2',
      errorMsg: '用户名长度不能小于2位',
    },
  ]);
  // 配置 密码 验证规则
  validator.add(formData.mima, [
    {
      strategy: 'minLength:6',
      errorMsg: '密码长度不能小于6位',
    },
  ]);
  // 配置 手机号码 验证规则
  validator.add(formData.phoneNumber, [
    {
      strategy: 'isMobile',
      errorMsg: '请输入正确的手机号码格式',
    },
  ]);
  // 开始校验
  return validator.start();
};

// 需要验证的表单对象
const TEST_USERINFOS = {
  userName: 'L',
  mima: '1',
  phoneNumber: '123456',
};

const result = validataFunc(TEST_USERINFOS);
console.log(result); // ['用户名长度不能小于2位', '密码长度不能小于6位', '请输入正确的手机号码格式']

3. 代理模式

代理对象和本体对象具有一致的接口

应用案例:图片预加载

// 本体图片
const relImage = (function () {
  const imageDOM = document.createElement('img');
  document.body.appendChild(imageDOM);
  return {
    setSrc(src) {
      imageDOM.src = src;
    },
  };
}());

// 代理图片
const proxyImage = (function () {
  const img = new Image();
  // 实际要加载的图片 加载成功后 替换调占位图
  img.onload = function () {
    relImage.setSrc(img.src);
  };
  return {
    setSrc(src) {
      img.src = src;
      // 设置占位图
      relImage.setSrc('https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg');
    },
  };
}());

// 设置实际要加载的图片
proxyImage.setSrc('https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg');

4. 装饰者模式

在不改变对象自身的基础上,动态地给某个对象添加一些额外的职责

应用案例:在函数执行前后添加新的方法

Function.prototype.before = function (beFn) {
  const self = this;
  return function () {
    // 先执行插入到前面的方法,类似于二叉树的前序遍历
    beFn.apply(this, arguments);
    // 后执行当前的方法
    return self.apply(this, arguments);
  };
};

Function.prototype.after = function (afFn) {
  const self = this;
  return function () {
    // 先执行当前的方法
    self.apply(this, arguments);
    // 后执行插入到后面的方法
    return afFn.apply(this, arguments);
  };
};

function fuc() {
  console.log(2);
}

function fuc1() {
  console.log(1);
}

function fuc3() {
  console.log(3);
}

function fuc4() {
  console.log(4);
}

fuc = fuc.before(fuc1).before(fuc4).after(fuc3);
fuc(); // 4 1 2 3

5. 组合模式

组合模式在对象间形成树形结构

  • 组合模式中,基本对象 和 组合对象被一致对待
  • 无须关心对象有多少层, 调用时只需在根部进行调用

应用案例: 打印文件目录

class Combine {
  constructor() {
    this.list = [];
  }

  add(fn) {
    this.list.push(fn);
    return this; // 链式调用
  }

  excute() {
    for (let i = 0; i < this.list.length; i++) {
      this.list[i].excute();
    }
  }
}

const comb1 = new Combine();
comb1
  .add({
    excute() {
      console.log(1);
    },
  })
  .add({
    excute() {
      console.log(2);
    },
  });

const comb2 = new Combine();
comb2
  .add({
    excute() {
      console.log(3);
    },
  })
  .add({
    excute() {
      console.log(4);
    },
  });

const comb3 = new Combine();
comb3
  .add({
    excute() {
      console.log(5);
    },
  })
  .add({
    excute() {
      console.log(6);
    },
  });
comb2.add(comb3);

const comb4 = new Combine();
comb4.add(comb1).add(comb2);
comb4.excute(); // 1 2 3 4 5 6

6. 工厂模式

工厂模式是用来创建对象的,将 “创建对象的具体逻辑” 封装在一个函数中,这个函数就可以被视为一个工厂

应用案例: jquery 中的 window.$

class Car {
  constructor(name, color) {
    this.name = name;
    this.color = color;
  }
}

class Factory {
  static create(type) {
    switch (type) {
      case 'car':
        return new Car('汽车', '白色');
        break;
      case 'bicycle':
        return new Car('自行车', '黑色');
        break;
      default:
        console.log('没有该类型');
    }
  }
}

let p1 = Factory.create('car');
let p2 = Factory.create('bicycle');

console.log(p1, p1 instanceof Car); // { name: '汽车', color: '白色' } true
console.log(p2, p2 instanceof Car); // { name: '自行车', color: '黑色' } true

7. 访问者模式

在不改变该对象的前提下,访问其结构中元素的新方法

应用案例:babel 插件

// 元素类
class Student {
  constructor(name, chinese, math, english) {
    this.name = name;
    this.chinese = chinese;
    this.math = math;
    this.english = english;
  }

  accept(visitor) {
    visitor.visit(this);
  }
}

// 访问者类
class ChineseTeacher {
  visit(student) {
    console.log(`语文${student.chinese}`);
  }
}

class MathTeacher {
  visit(student) {
    console.log(`数学${student.math}`);
  }
}

class EnglishTeacher {
  visit(student) {
    console.log(`英语${student.english}`);
  }
}

// 实例化元素类
const student = new Student('张三', 138, 150, 146);
// 实例化访问者类
const chineseTeacher = new ChineseTeacher();
const mathTeacher = new MathTeacher();
const englishTeacher = new EnglishTeacher();
// 接受访问
student.accept(chineseTeacher); // 语文 138
student.accept(mathTeacher); // 数学 150
student.accept(englishTeacher); // 英语 146

8. 发布订阅模式

订阅者订阅相关主题,发布者通过发布主题事件的方式,通知订阅该主题的对象

应用案例:EventBus

// 发布订阅模式
class EventBus {
  constructor() {
    this.task = {};
  }
  on(type, fn) {
    // on 注册事件
    if (!this.task[type]) this.task[type] = [];
    this.task[type].push(fn);
  }
  emit(type, ...args) {
    // emit 发送事件
    if (this.task[type]) {
      this.task[type].forEach(fn => {
        fn.apply(this, args); // 注意this指向
      });
    }
  }
  off(type, fn) {
    // 删除事件
    if (this.task[type]) {
      this.task[type] = this.task[type].filter(item => item !== fn);
    }
  }
  once(type, fn) {
    // 只执行一次
    function f(...args) {
      fn(...args);
      this.off(type, f);
    }
    this.on(type, f);
  }
}

// 测试
let event = new EventBus();

event.on("change", (...args) => {
  console.log(args);
});

// 只执行一次
event.once("change", (...args) => {
  console.log(args);
});

event.emit("change", 1, 2);
event.emit("change", 2, 3);

9. 观察者模式

一个对象有一系列依赖于它的观察者(watcher),当对象发生变化时,会通知观察者进行更新

应用案例: vue 双向绑定

const data = {
  name: 'ming',
  age: 18,
};

Object.keys(data).forEach((key) => {
  let value = data[key];
  Object.defineProperty(data, key, {
    get() {
      console.log('get', value);
      return value;
    },
    set(newValue) {
      console.log('更新');
      value = newValue;
    },
  });
});

data.name = '佩奇';

console.log(data.name); // 更新 → get 佩奇 → 佩奇

观察者模式 VS 发布订阅模式

观察者模式:一个对象有一系列依赖于它的观察者(watcher),当对象发生变化时,会通知观察者进行更新

发布订阅模式:订阅者订阅相关主题,发布者通过发布主题事件的方式,通知订阅该主题的对象,发布订阅模式中,可以基于不同的主题,去执行不同的自定义事件

10. 参考链接

javaScript设计模式统计 - 知乎大部分讲设计模式的文章都是使用的Java、C++这样的以类为基础的静态类型语言,作为前端开发者,js这门基于原型的动态语言,函数成为了一等公民,在实现一些设计模式上稍显不同,甚至简单到不像使用了设计模式,有…https://zhuanlan.zhihu.com/p/472719016

JavaScript 中常见设计模式整理 - 掘金开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知。 看完了上述设计模式后,把它们的关键词特点罗列出来,以后提到某种设计模式,进而联想相应的关键词和例子,从而心中有数。https://juejin.cn/post/6844903607452581896

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/100428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS 7安装及使用MobaXterm连接

1、 下载centos7映像文件地址&#xff1a;http://mirrors.aliyun.com/centos/7/isos/x86_64 选择CentOS-7.0-x86_64-DVD-2009.iso 标准安装版或者CentOS-7-x86_64-Everything-1908.iso下载 2、安装centos7 2.1、右击以管理员身份运行 2.2、点击创建新的虚拟机 2.3、选择…

c++ - 第18节 - 哈希

1.unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c;进行很…

MockServer 服务框架设计

大部分现有的 mock 工具只能满足 HTTP 协议下简单业务场景的使用。但是面对一些复杂的业务场景就显得捉襟见肘&#xff0c;比如对 socket 协议的应用进行 mock&#xff0c;或者对于支付接口的失败重试的定制化 mock 场景。 为解决上述问题&#xff0c;霍格沃兹测试学院设计并研…

【Redis -String、List介绍和应用场景】

String String 是最基本的 key-value 结构&#xff0c;key 是唯一标识&#xff0c;value 是具体的值&#xff0c;value其实不仅是字符串&#xff0c; 也可以是数字&#xff08;整数或浮点数&#xff09;&#xff0c;value 最多可以容纳的数据长度是 512M。 内部实现 String 类…

万字解析,带你深入掌握多种排序算法!-C语言

今天我们来看排序&#xff0c;排序在生活中经常使用&#xff0c;非常重要&#xff0c;是必学的内容。 目录 1.插入排序 1.1直接插入排序 1.2希尔排序 2.选择排序 2.1直接选择排序 2.2堆排序 3.交换排序 3.1冒泡排序 3.2快速排序 3.2.1挖坑法 3.2.2左右指针法 3.2.3…

【驯服野生verilog-mode全记录】day4 —— 对循环展开语法的python脚本外挂支持

我们的目标是┏ (゜ω゜)☞芯片前端全栈工程师~喵&#xff01; 系列文章目录 【驯服野生verilog-mode全记录】day3 —— 基于vim自动生成verilog-mode格式初始文件模板_尼德兰的喵的博客-CSDN博客 【驯服野生verilog-mode全记录】day2 —— 模块的例化_尼德兰的喵的博客-CSDN…

微服务框架 SpringCloud微服务架构 服务异步通讯 53 MQ 集群 53.4 仲裁队列【RabbitMQ控制台搭建】

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 服务异步通讯 文章目录微服务框架服务异步通讯53 MQ 集群53.4 仲裁队列【RabbitMQ控制台搭建】53.4.1 仲裁队列53.4.2 搭建仲裁队列53 MQ 集…

10. 注解开发Bean作用范围和生命周期管理

1. bean作用范围注解配置 使用Scope注解定义bean作用范围 1.1. singleton为单例 1.1.1 在bean头上使用Scope注解&#xff0c;singleton package com.lin.dao.impl;import com.lin.dao.BookDao; import org.springframework.context.annotation.Scope; import org.springfra…

企业档案管理实务:档案的检索方法知多少

在鸿翼档案的企业档案系统设计中&#xff0c;企业档案常用的检索实际上包括两个行为&#xff1a;企业档案信息的贮存和企业档案信息的查找。档案检索工具一方面是整个企业档案检索体系中贮存结果的最终体现&#xff0c;直接反映贮存的质量和水平&#xff1b;另一方面又是各项业…

Redis 哈希(Hash)方法使用详解

目录一、简介二、常用方法2.1、HSET2.2、HSETNX2.3、HGET2.4、HINCRBY、HINCRBYFLOAT2.5、HSTRLEN2.6、HEXISTS2.7、HDEL2.8、HLEN2.9、HMSET、HMGET2.10、HKEYS、HVALS、HGETALL2.11、HSCAN一、简介 本文今天主要是讲哈希&#xff08;Hash&#xff09;的方法的使用&#xff0c…

毕业设计 单片机手势检测识别系统 - arduino 物联网 嵌入式

文章目录0 前言1 简介2 主要器件3 实现效果4 设计原理5 部分核心代码6 最后0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长…

uniapp中video层级太高(或者在页面中不跟随页面滚动)解决方案

我觉得这个问题有必要记录一下。最近项目中遇到的问题&#xff1a;项目是uniapp开发&#xff0c;有一个商品详情的页面和一个视频竖向轮播的页面。 问题描述 1、商品详情页上面是图片轮播(包含视频)&#xff0c;下面是商品详情&#xff0c;当页面上下滑动时&#xff0c;如果当…

微服务框架 SpringCloud微服务架构 服务异步通讯 52 惰性队列 52.2 惰性队列

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 服务异步通讯 文章目录微服务框架服务异步通讯52 惰性队列52.2 惰性队列52.2.1 惰性队列52.2.2 总结52 惰性队列 52.2 惰性队列 52.2.1 惰…

【BTC】Fork

区块链中fork的分类&#xff1a; state fork&#xff1a; 两个节点差不多同一个时候挖到了矿&#xff0c;就会出现一个临时性的分叉。 forking attack&#xff08;deliberate fork&#xff09;&#xff1a;也是属于对比特币这个区块链当前的状态产生的意见分歧&#xff0c;只不…

汇编语言笔记——汇编程序开发、汇编大作业

文章目录传送门汇编程序开发汇编代码初步实践dos虚拟机与挂载实模式Hello World代码解析汇编链接重新汇编与批处理反汇编与debug保护模式Hello WorldVS2017masm32环境配置汇编基础语言概述汇编环境汇编语言语句格式常用伪指令数据定义符号定义操作符\$OFFSET算术操作符逻辑操作…

【基础】RabbitMQ 基础

RabbitMQ 基础RabbitMQ 基本介绍RabbitMQ 基本概念ConnectionChannelVirtual HostQueueExchangedirectfanouttopicheadersRabbitMQ 基本介绍 RabbitMQ 是使用 Erlang 语言开发的基于 AMQP 协议来实现的开源消息队列系统。 RabbitMQ 的特点&#xff1a; 灵活路由&#xff1a;E…

社招前端二面react面试题整理

解释 React 中 render() 的目的。 每个React组件强制要求必须有一个 render()。它返回一个 React 元素&#xff0c;是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素&#xff0c;则必须将它们组合在一个封闭标记内&#xff0c;例如 <form>、<group>、<div&g…