首页 > 其他分享 >[JS]设计模式

[JS]设计模式

时间:2024-07-09 08:59:39浏览次数:28  
标签:function console log 迭代 JS const 设计模式 event

介绍

设计模式就是在 面向对象软件 设计过程中, 针对特定问题的简洁而优雅的解决方案

目前说到设计模式, 一般指<设计模式: 可复用面向对象软件的基础>一书中提到的23种常见软件设计模式

工厂模式

在JavaScript中, 工厂模式的表现形式就是一个 调用即可返回新对象 的函数

<script>
    //  1.普通函数
    function FoodFactory(name, color) {
      return {
        name,
        color
      }
    }
    const f1 = FoodFactory('apple', 'red')

    // 2.构造函数
    function Food(name, color) {
      this.name = name
      this.color = color
    }
    const f2 = new Food('apple', 'red')
  </script>

<script>
    // 1.vue3 - createApp
    import { createApp } from 'vue'
    const app = createApp({})
  </script>

优势

  1. 避免在测试期间, 全局配置污染其它测试用例
  2. 全局改变Vue实例的行为,移到Vue实例上

<script>
    // 2. axios-create
    import axios from 'axios'
    const instance = axios.create({
      baseURL: 'http://localhost:3000',
      timeout: 1000
    })
</script>

单例模式

单例模式就是保证整个系统只有一个对象存在

<script>
    //  实例单例
    // 1.定义类
    class SingleTon {
      // 2.定义私有属性, 用于保存实例对象
      static #instance

      // 3.定义静态方法, 用于创建实例对象
      static getInstance() {
        // 4.判断
        if (this.#instance === undefined) {
          this.#instance = new SingleTon()
        }

        // 5.返回对象
        return this.#instance
      }
    }

    // 测试代码
    const s1 = SingleTon.getInstance()
    const s2 = SingleTon.getInstance()
    console.log(s1 === s2) // ture
  </script>

实际应用

单例方法

  • vant中的toast和notify组件

单例思想

  • vue2中的use方法
  • vue3中的use方法

观察者模式

在对象之间定义一个 一对多 的依赖, 当一个对象状态发生改变的时候, 所有依赖的对象都会自动收到通知

发布订阅模式

发布订阅模式在发布者和订阅者之间建立联系, 发布者发布消息, 通过事件总线, 通知所有订阅者

发布订阅vs观察者:

  • 发布订阅模式有中间商, 发布者与订阅者没有直接联系
  • 观察者模式没有中间商, 观察者与目标对象直接联系

应用场景: EventBus

  • vue2: 直接使用实例方法($on, $emit, $off, $once)
  • vue2: 使用第三方插件
<body>
  <h2>发布订阅模式</h2>
  <button class="on">注册事件</button>
  <button class="emit">触发事件</button>
  <button class="off">移除事件</button>
  <button class="once-on">一次性事件注册</button>
  <button class="once-emit">一次性事件触发</button>

  <script>
    class EventEmitter {
      #handlers = {
        //事件名: [callback1, callback2] 
      }

      /**
       * 注册事件监听
       * 1.1添加私有属性
       * 1.2保存事件
      */
      $on(event, callback) {
        if (!this.#handlers[event]) {
          this.#handlers[event] = []
        }

        this.#handlers[event].push(callback)
      }


      /**
       * 触发事件
       * 2.1接受不定长参数
       * 2.2循环触发事件
      */
      $emit(event, ...args) {
        const funcs = this.#handlers[event] || []
        funcs.forEach(callback => {
          callback(...args)
        });
      }

      /**
       * 移除事件
       * 3.1清空事件
      */
      $off(event) {
        this.#handlers[event] = undefined
      }

      /**
       * 一次性事件
       * 4.1调用$on注册事件
       * 4.2事件内调用$off移除事件
      */
      $once(event, callback) {
        this.$on(event, (...args) => {
          callback(...args)
          this.$off(event)
        })
      }
    }

    // 测试代码
    const event = new EventEmitter()

    // 注册事件
    document.querySelector('.on').addEventListener('click', function () {
      event.$on('event1', () => { console.log('1111'); })
      event.$on('event1', () => { console.log('2222'); })
      event.$on('event2', () => { console.log('3333'); })
    })
    // 触发事件
    document.querySelector('.emit').addEventListener('click', function () {
      event.$emit('event1')
      event.$emit('event2')
    })
    // 移除事件
    document.querySelector('.off').addEventListener('click', function () {
      event.$off('event1')
    })
    // 一次性事件注册
    document.querySelector('.once-on').addEventListener('click', function () {
      event.$once('event3', () => { console.log(4444); })
    })
    // 一次性事件触发
    document.querySelector('.once-emit').addEventListener('click', function () {
      event.$emit('event3')
    })
  </script>
</body>

原型模式

原型模式是创建型模式的一种, 其特点在于通过 复制 一个已经存在的实例来返回新的实例, 而不是新建实例

<script>
    const food = {
      name: '西蓝花',
      eat() {
        console.log('好好吃');
      }
    }
    // 复制新对象
    // Object.create 将对象作为原型, 创建新对象
    const f = Object.create(food);
    console.log(f === food);  // fasel
  </script>

应用: Vue2源码中, 对于数组变更方法的封装, 使用到了该模式

代理模式

代理模式是为一个对象提供一个代理, 以便控制对它的访问

缓存代理

<body>
  <button id="btn">发起请求</button>
  <script>
    /**
     * 缓存代理
     * 需求: 第一次查询的数据通过接口获取, 重复查询通过缓存获取
     */

    // 1创建对象缓存数据
    const cache = {}

    document.querySelector('#btn').addEventListener('click', async () => {
      // 2判断是否缓存数据
      console.log(cache);
      if (!cache.pname) {
        const search = new URLSearchParams({ pname: '广东省' }).toString()
        const res = await fetch('http://hmajax.itheima.net/api/city?' + search)
        const data = await res.json()
        // 3缓存数据
        cache.pname = data.list
      }

      // 4返回数据
      return cache.pname
    })
  </script>
</body>

迭代器模式

可以让用户透过特定的接口巡防容器中的每一个元素而不用了解底层的实现(遍历)

<script>
  Object.prototype.objFunc = function () { }
  Array.prototype.arrFunc = function () { }
  const foods = ['西蓝花', '菜花', '西葫芦']

  /**
     * for in
     * 遍历一个对象(除了Symbol)的属性(可枚举), 包括继承的可枚举属性
     * 遍历的出是索引
     * 继承来的也可以遍历出来(原型链上动态增加的也可以遍历)
    */
  for (let key in foods) {
    console.log('key', key) //0,1,2,objFunc,arrFunc
  } 
</script>

<script>
  Object.prototype.objFunc = function () { }
  Array.prototype.arrFunc = function () { }
  const foods = ['西蓝花', '菜花', '西葫芦']

  /**
     * for of
     * for of可以遍历可迭代对象
     * 包括 Array Map Set String TypeArray Argumente ... ...
     * 无法遍历 Object
     * 遍历的是值
     * 继承来的不会遍历出来
    */
  for (let food of foods) {
    console.log('food', food) //西蓝花,菜花,西葫芦
  }

</script>

迭代协议

迭代协议可以制定对象的迭代行为, 遵循协议的对象都是可迭代对象, 迭代器协议分为 可迭代协议 和 迭代器协议, 只要对象循序这两个协议之一, 就可以被for of遍历

<script>
  // 可迭代协议:
  // 增加方法[Symbol.iterator](){}
  // 返回可迭代对象
  const obj = {

  // Symbol.inerator 内置的常量
  // [属性名表达式]
  [Symbol.iterator]() {
    // 使用Generator实现可迭代协议
    function* foodGenerator() {
      yield '西蓝花'
      yield '菜花'
      yield '西葫芦'
    }
    const food = foodGenerator()
    return food
  }
}

for (const iterator of obj) {
  console.log('iterator', iterator);  // 西蓝花,菜花,西葫芦
}

</script>

<script>
    // 迭代器协议: 
    // 有 next方法的对象, next方法返回:
    // 已结束: {done: true}
    // 继续迭代: {done: false, value: 'x'}
    const obj = {

      // 自己编写next方法实现迭代器协议
      [Symbol.iterator]() {
        let index = 0
        const arr = ['西蓝花', '菜花', '西葫芦']

        return {
          next() {
            if (index < arr.length) {
              // 继续迭代
              return { done: false, value: arr[index++] }
            } else {
              // 迭代完毕
              return { done: true }
            }
          }
        }
      }
    }

    for (const iterator of obj) {
      console.log('iterator', iterator); //西蓝花,菜花,西葫芦
    }

  </script>

标签:function,console,log,迭代,JS,const,设计模式,event
From: https://blog.csdn.net/CSDN20221005/article/details/140258308

相关文章

  • 05-JS中的内置类
    01原始类型的调用![[06-javascript基础/imgs/00041.png]]02Number包装类型![[06-javascript基础/imgs/00043.png]]示例1<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatib......
  • json数据写入到mysql数据中
    importpymysql#json文件中格式一个列表包含一个个的字典数据#[{"title":"胖猫事件-21岁游戏代练胖猫跳江身亡,PUA捞女女主谭竹遭网友报告视频","cover":"https://suvip888.com/20240516/U8NEMN2P/1.jpg","m3u8_url":"https://vodvip888.com/20240516/U8NEMN2P/......
  • 【js面试题】深入理解尾递归及其在JavaScript中的应用
    面试题:举例说明尾递归的理解,以及应用场景引言:在编程中,递归是一种常见的解决问题的方法,它允许函数调用自身来解决问题。然而,递归如果不当使用,可能会导致栈溢出错误,特别是在处理大量数据时。尾递归是一种特殊的递归形式,它能够优化递归调用,避免栈溢出的问题。本文将深入探......
  • 【JSP+Servlet+Maven】——优质外卖订餐系统之概论部分
    ......
  • 前端面试题29(js闭包和主要用途)
    JavaScript中的闭包是一个非常强大的特性,它允许一个函数访问并操作其词法作用域之外的变量。闭包的形成主要依赖于函数的作用域链,即函数可以访问在其外部定义的变量,即使外部函数已经执行完毕。下面我会通过几个方面来帮助你理解闭包的概念:闭包的定义闭包是一个函数及其......
  • Maven工程下:alibaba fastjson2的各种序列化:java对象转json对象、json对象转java对象
    pom文件导入fastjson2坐标:<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.51</version></dependency>UserVO对象:@Data@AllArgsConstructor......
  • java比较json对象是否相等
    一、需求需要对比这2个json字符串是否完全一样(不用管顺序)1Stringdui="{\"adGroupVO\":{\"campaignId\":\"CAMPAIGN201912101000004559\",\"adGroupChannel\":{\"channelType\":\"SMS\",\"resourceCode\&......
  • [NodeJS] Streams流式数据处理
    在现代应用开发中,数据处理的效率和资源管理尤为重要。NodeJS作为一种高效的JavaScript运行时环境,通过其强大的流(Stream)功能,提供了处理大规模数据的便捷方式。流式数据处理不仅能够优化内存使用,还可以提高数据处理的实时性和效率。下文将介绍NodeJS中的流概念、流的类型以及如何利......
  • 获取Echarts的geoJson文件(省市/区县)
    1.获取市的级别直接使用阿里云提供的工具直接获取: https://datav.aliyun.com/portal/school/atlas/area_selector#&lat=32.62087018318113&lng=118.43261718749999&zoom=4和 https://map.easyv.cloud/和 https://geojson.hxkj.vip/  2.获取县和区的细到街道的geoJson,需......
  • 前端面试基础html/js/css
    一、css1.说一下css盒子模型CSS盒子模型(BoxModel)是CSS中用于描述元素尺寸和布局的一个重要概念。它定义了元素的内容、内边距、边框、外边距和高度的计算方式。盒子模型对于网页布局和响应式设计至关重要。在CSS中,每个元素都可以被视为一个盒子,这个盒子由内容(content)、......