• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
RatherBe
博客园    首页    新随笔    联系   管理    订阅  订阅

JavaScript设计模式

JavaScript设计模式

设计模式概念

经过代码设计经验总结之后设计出的一种固定解决问题的方式

设计模式作用

  • 代码复用

  • 保证代码可靠性

  • 将编程工程化

  • 更易被他人理解

设计模式的分类(W3C平台)

构造器模式,模块化模式,暴露模块模式,单例模式,中介者模式,原型模式,命令模式,外观模式,工厂模式,Mixin模式,装饰模式,亨元(Flyweight)模式,MVC模式,MVP模式,MVVM模式,组合模式,适配器模式,外观模式,观察者模式,迭代器模式,惰性初始模式,代理模式,建造者模式,...

常用设计模式

1. 单例模式

概念 :

多次操作是在同一个实例对象上实现 即第一次为创建实例对象 后面的都是在原有的实例对象上操作

优点 :

节省性能 提升执行速度

  function Fn(){
      // 这里自定义__obj__是为了防止和Fn内置对象名重叠
      if(!Fn.__obj__){
          // 这里没有这个对象存在再创建 有则不创建
          // 保证单例的核心
          Fn.__obj__ = {};
      }
      Fn.__obj__.name = "admin";
      return Fn.__obj__;
  }

2. 工厂模式

概念 :

多次创建多个具有相同属性名相同方法功能的不同实例对象

工厂模式的标志 :

原料(创建基础对象) 加工(给基础对象添加属性或方法) 出厂(将基础对象返回到外部)

优点 :

相互独立 分别控制 互不干扰

  function Fn(name, age){
    this.name = name;
    this.age = age
  }
  const f1 = new Fn("admin", 18)
  const f2 = new Fn("root", 19)
  // 工厂模式和单例模式不同 每次创建的都是新实例对象
  console.log(f1 === f2);
  // false

3. 抽象工厂模式

概念 :

在工厂模式的基础上进行二次封装,将相同的属性值再次封装

  // 造车厂
  function CreateCar(brand, color, type){
    this.brand = brand;
    this.color = color;
    this.type = type;
  }

  // 专门用来生产比亚迪的生产线
  function BYDCar(color, type){
    return new CreateCar("比亚迪", color, type);
  }

  const b1 = new BYDCar("白色", "SUV");
  const b2 = new BYDCar("红色", "轿车");

  // 专门用来生产大众的生产线
  function WCar(color, type){
    return new CreateCar("大众", color, type);
  }

  const w1 = new WCar("黑色", "SUV");
  const w2 = new WCar("灰色", "轿车");

4. 适配器模式

概念 :

解决两个软件实体间的接口不兼容的问题,对不兼容的部分进行适配

例 : 手机没有3.5耳机插口,所以就需要增加一个转接头来完成适配功能以确保耳机的正常使用

  // 创建一个手机构造器 有打电话打游戏功能
  function CreatePhone(){
    this.name = "手机"
    this.call = function(){
      console.log("打电话")
    }
    this.game = function(){
      console.log("玩游戏")
    }
  }

  // 创建一个电脑构造器 有游戏功能
  function CreateComputer(){
    this.name = "电脑"
    this.game = function(){
      console.log("玩游戏")
    }
  }

  // 测试打电话和游戏功能
  function test(obj){
    obj.call();
    obj.game();
  }

  const p = new CreatePhone()
  const c = new CreateComputer()

  // 如果没有适配器 电脑因为没有打电话功能test会报错
  function Adapter(obj){
    // 如果没有这个功能则定义一个这个功能
    if(!obj.call){
      obj.call = function(){
        console.log("这是" + obj.name + "没有打电话功能")
      }
    }
    return obj;
  }

  test(p);
  // 打电话
  // 玩游戏
  test( Adapter(c) );
  // 这是电脑没有打电话功能
  // 玩游戏

  console.log(p)
  console.log(c)

5. 代理模式

概念

不直接访问对象 而是提供一个中间对象(代理)来控制对这个对象的访问

生活中也有比较常见的代理模式:中介、寄卖、经纪人等等

  // 发送数据方 target为接收方 name为发送方名字
  function sender(target, name){
    this.name = name;
    // 定义发送方式
    this.send = function(msg){
      console.log(this.name + "将" + msg + "交给了" + target.name);
    }
  }

  // 接收数据方 name为接收方名字
  function reciver(name){
    this.name = name;
  }

  // 创建'bob'实例对象用于接收数据
  const s = new reciver("bob");

  // 定义中间代理 target为接收方
  function poster(target){
    // 创建发送方实例对象
    const f = new sender(s, "tom");
    //message用于存储各种数据
    this.message = [];
    this.send = function(msg){
      this.message.push({
        发件人: f.name,
        收件人: target.name,
        物品: msg,
        时间: Date.now()
      })
      //调用发送方send方法 msg为传进来参数
      f.send(msg);
    }
  }

  // 创建代理实例对象
  const k = new poster(s);
  // 代理发送方tom发送数据给接收方bob
  k.send("一批教材");
  k.send("一批教具");
  k.send("一批学生");

  console.log(k.message);

6. 观察者模式

又称发布订阅模式(Publish/Subscribe)

概念

观察者模式定义了一种一对多的关系 让一个对象(发布者Dep)能被多个观察者(订阅者Observer)同时监听

优点

  • 支持简单的广播通信 自动通知所有订阅者
  • 页面载入后发布者很容易与观察者存在一种动态关联 增加了灵活性
  • 发布者与观察者之间的抽象耦合关系能够单独扩展以及重用(解耦)
  function MyEvent() {
    // 定义一个对象存储dep和数组形式的observer
    this.message = {};

    // 绑定发布者和目标订阅者
    this.binding = function(dep, observer) {
      if (this.message[dep]) {
        // 发布者存在 则加入新的订阅者
        this.message[dep].push(observer);
      } else {
        // 发布者不存在 则创建一个数组 存入第一个订阅者
        this.message[dep] = [observer];
      }
    }

    // 绑定发布者和目标订阅者
    this.unbinding = function(dep, observer) {
      // 发布者不存在 则返回
      if (!this.message[dep]) return;
      // 找到订阅者在数组中的索引
      var index = this.message[dep].indexOf(observer);
      if (index != -1) {
        // 如果有 则从索引位置删除他
        this.message[dep].splice(index, 1);
      }
    }
    
    // 执行目标发布者的所有订阅者的行为
    this.emit = function(dep) {
      // 发布者不存在 则返回
      if (!this.message[dep]) return;
      // 调用所有订阅者方法
      this.message[dep].forEach((val) => {val(dep);})
    }
  }

  // 创建实例对象
  const event = new MyEvent();

  // 绑定发布者和订阅者
  event.binding('bob', follower1);
  event.binding('bob', follower2);

  event.binding('tom', follower1);
  event.binding('tom', follower2);

  // 解绑发布者和目标订阅者
  event.unbinding('tom', follower1);

  // 执行目标发布者所有订阅者行为
  event.emit('bob');
  event.emit('tom');

  // 定义订阅者行为
  function follower1(dep) {
    console.log('follower1订阅了' + dep)
  }
  function follower2(dep) {
    console.log('follower2我订阅了' + dep)
  }

7. 策略模式

概念 :

将多个功能封装起来 定义一系列算法 并使他们能直接相互替换

优点 :

  • 利用组合 委托 避免了条件语句
  • 使代码更易理解和扩展
  • 代码复用
  // 需求: 绩效计算
  // 条件选择方式 if分支随着绩效分类增多而增多 影响性能
  let bonus = function (performance, salary) {
    if(performance == "S") {
      return salary * 4;
    } else if (performance == "A") {
      return salary * 3;
    } else if (performance == "B")
      return salary * 2;
  }

  // 策略模式
  
  let calculateBonus = {
    "S": function ( salary ){
      return salary * 4;
    },
    "A": function ( salary ) {
      return salary * 3;
    },
    "B": function ( salary ) { 
      return salary * 2;
    }
  }

  function calculate(level, salary) {
    return calculateBonus[level](salary);
  }

  console.log(calculate('S', 20000) + '$')

8. MVC模式

全名: Model View Controller 模型 视图 控制器

  • M: 模型 按照要求来取出数据
  • V: 视图 用户直观看到的页面
  • C: 控制器 向系统发出指令的工具

优点

  • 降低代码耦合
  • 分工合作 提高开发效率
  • 组件重用

工作流程

  1. 用户的请求提交给控制器

  2. 控制器接收到用户请求后根据用户的具体需求 调用相应的程序来处理用户的请求

  3. 控制器调用程序处理完数据后 将数据显示出来

  // 定义模型 按照要求读取数据
  class Model {
    m1() {
      return 'Model1';
    }
    m2() {
      return 'Model2';
    }
  }

  // 定义视图 用于数据展示
  class View {
    v1(m) {
      console.log(m);
    }
    v2(m) {
      document.write(m);
    }
  }

  // 根据控制器找到对应数据
  class Control {
    constructor() {
      this.m = new Model();
      this.v = new View();
    }
    c1() {
      let value = this.m.m1();
      this.v.v1(value);
    }
    c2() {
      let value = this.m.m2();
      this.v.v2(value);
    }
  }

  // 创建一个控制器实例对象
  const c = new Control();

  // 发出指令
  c.c1();
  c.c2();

9. 组合模式

概念 :

把多个对象组成树状结构来表示局部与整体,使得用户可以同时操作单个对象或对象的组合

优点 :

  • 组合模式可以非常方便地描述对象部分-整体层次结构
  • 组合模式将一批子对象组织为树形结构 一条根命令能操作下面所有子元素
  // <style>
  //   img{width:100px;}
  //   *{margin: 10px;padding: 10px;}
  // </style>

  // 枝
  class Team {
    constructor(id) {
      // 组合模式核心之一 使得每个元素都能保存自己所有的子元素
      this.children = [];
      this.ele = document.createElement('div');
      this.ele.id = id;
    }
  
    add(child) {
      // 在数组中加入child元素
      this.children.push(child);
      // 将child添加到当前实例对象对应元素(this.ele)中
      this.ele.appendChild(child.ele);
    }

    remove(child) {
      // 找到这个child元素在数组中索引
      let c = this.children.indexOf(child);
      // 根据索引删除元素
      this.children.splice(c, 1);
      // child为实例对象 找到实例对象对应元素 并删除节点元素
      child.ele.remove();
    }

    addBorder() {
      // 给当前实例对象对应元素添加边框
      this.ele.style.border = '2px solid black';
      // 以下递归和核心之一
      // 给当前实例对象children数组(保存所有子元素对应实例对象)中所有元素也添加边框 如果子实例对象对应实例对象中还有实例对象则递归进去 重复之前步骤 直到所有子孙元素
      this.children.forEach(val => {val.addBorder()});
    }

    removeBorder() {
      // 给当前实例对象对应元素删除边框
      this.ele.style.border = 'none';
      // 原理同addBorder
      this.children.forEach(val => {val.removeBorder()});
    }
  }

  // 叶
  class Item {
    constructor(src) {
      // 此处ele为核心之一 这里的ele必须和上面枝中的ele相同 如果不同 this.ele在递归到叶中实例对象时 因找不到ele元素而报错而报错
      this.ele = document.createElement('img');
      this.ele.src = src;
    }
    add() {
      console.log('此为叶节点 不能添加');
    }
    remove() {
      console.log('此为叶节点 不能删除');
    }
    addBorder() {
      // 叶没有子元素 所以只要给自己添加边框
      this.ele.style.border = '2px solid red';
    }
    removeBorder() {
      // 叶没有子元素 所以只要给自己删除边框
      this.ele.style.border = 'none';
    }
  }

  // 创建叶实例对象
  const img1 = new Item("https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF");
  const img2 = new Item("https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF");
  const img3 = new Item("https://t7.baidu.com/it/u=2511982910,2454873241&fm=193&f=GIF");
  const img4 = new Item("https://t7.baidu.com/it/u=3435942975,1552946865&fm=193&f=GIF");

  // 创建枝对象
  const box1 = new Team("box1");
  const box2 = new Team("box2");
  const box3 = new Team("box3");
  const box4 = new Team("box4");
  const box5 = new Team("box5");

  // 将盒子 和 img组合
  box1.add(box2);
  box1.add(box3);
  box3.add(box4);
  box4.add(box5);

  box1.add(img1);
  box4.add(img2);
  box4.add(img3);
  box5.add(img3);

  // 以下为box在body中排列
    //   box1
    //     box2
    //     box3
    //       box4
    //         box5
              // img3
          //   img2
          //   img3
          // img1

  // 将根box1添加到body中
  document.body.appendChild(box1.ele);

  // 给元素添加删除边框
  box1.addBorder();
  box3.addBorder();

  img2.removeBorder();
posted @ 2023-04-26 09:06  泡椒不辣  阅读(26)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3