JS设计模式

2023-11-13

目录

前言

单例设计模式

Command 命令模式

Constructor构造器模式

工厂模式Factory

发布订阅设计模式 publish&subscribe

观察者模式 

中介者模式


前言

JS设计模式是一种思想,更规范更合理的管理代码(方便维护,升级,扩展,开发)

单例设计模式

单例设计模式Singleton && Command 命令模式

  • 最早期的编程化思想(同样的还有:AMD/CMD/commoJS/ES6Module)
  • 避免全局变量污染,
  • 实现模块之间的相互调用(提供了模块导出方案)
    //Object
    let obj = {}

    //------高级单例------
    //公共模块 utils
    let utils = (function () {
        function throttle(func, wait) {
            //....
        }
        return {
            throttle: throttle;
        }
    })()

    // a模块
    let AModule = (function () {
        utils.throttle();
        function fn() { };
        function query() { };
        return {
            query: query
        }
    })()
    //b模块
    let BMoudle = (function () {
        utils.throttle();
        function fn() { };
        AModule.query();
        return {}
    })()

Command 命令模式

在实际的开发过程中,我们还可以基于命令模式管控方法的执行顺序,从而有效的实现出对应的功能

    //b模块(实现当前模块下需要完成的所有功能)
    let BMoudle = (function () {
        utils.throttle();
        AModule.query();

        //获取数据的
        function getData() { };
        // 绑定数据的
        function binding() { };
        //处理事件的
        function handle() { };
        //处理其他事情的
        function fn() { };
        return {
            //模块的入口(相当于模块的大脑,控制模块的顺序)
            init() {
                getData();
                binding();
                handle();
                fn();
            }
        }
    })();
    BMoudle.init()

Constructor构造器模式

自定义类和实例 ,(站在面向对象的思想上去构建项目)

私有&公有的属性和方法,

编写公共类库&写一些插件组件,

每一次调用插件我们都是创造这个类的实例,即保证了每个实例之间,有自己的私有属性,互不影响也可以保证一些方法属性还是公用的,有效的避免了代码冗余

  // ES5
    function Fn() {
        this.xxx = xxx;
    }
    Fn.prototype = {
        constructor: Fn,
        query() { },
        //..
    }
    Fn.xxx = xxx;
    //es6 
    class Fn {
        constructor() {
            this.xxx = xxx;
        }
        query() { };
        static xxx() { };
    }
    let f1 = new Fn;
    let f2 = new Fn;

工厂模式Factory

简单的工厂模式(一个方法根据传递的参数不同,做了不同的处理)

   function factory(options) {
        if (options === null) options = {};
        if (!/^(object|function)$/i.test(typeof options)) options = {};
        let { type, payload } = options;
        if (type = 'MYSQL') {
            //...
            return;
        };
        if (type = 'SQLSERVER') {
            //....
            return;
        }
    }
    factory({
        type: 'SQLSERVER',
        payload: {
            root: '',
            pass: '',
            select: ''
        }
    })

JQ中的工厂模式(加工转换)

 (function () {
        function jQuery(selector, content) {
            return new jQuery.fn.init(selector, content)
        }
        jQuery.fn = jQuery.prototype = {
            constructor: jQuery,
            //.....
        };

        //中间转换
        function init(selector, content, root) { }
        jQuery.fn.init = init;
        init.prototype = jQuery.fn;

        if (typeof window !== 'undefined') {
            window.$ = w.jQuery = jQuery
        }
    })()
    // $()--->jQuery的实例

发布订阅设计模式 publish&subscribe

自定义事件的一种方案 灵感来源于 addEventListener DOM2事件绑定,

  • 给当前元素的某一个事件行为,绑定多个不同的方法(事件池机制)
  • 当事件行为触发的时候,会依次通知事件池中的方法执行
  • 支持内置事件(标准事件,)例如 click dbclick mouseenter...

扫盲:dom0级事件绑定原理:给元素对象对应的事件行为的私有属性赋值,dom2往事件池里添加方法

简单版的发布订阅

弊端:只有一个事件池,只能应用于一个场景

 (function () {
        //自己创造的事件池
        let pond = [];

        //向事件池中注入方法
        function subscribe(func) {
            //去重处理
            if (!pond.includes(func)) {
                pond.push(func)
            }
            //每一次执行,返回的方法是用来移除当前这个新增的这个方法的
            return function unsubscribe() {
                pond = pond.filter(item => item !== func)
            }

        }
        //通知事件池中的方法执行
        subscribe.fire = function fire(...params) {
            pond.forEach(item => {
                if (typeof item === 'function') {
                    item(...params)
                }
            })
        }
        window.subscribe = subscribe
    })();
    let unsubscribe1 = subscribe(function () {
        console.log(1, arguments);
    });
    subscribe(function () {
        console.log(2, arguments);
    });
    subscribe(function () {
        console.log(3);
        unsubscribe1();
    });
    subscribe(function () {
        console.log(4);
    });
    setTimeout(() => {
        subscribe.fire(10, 20, 30)
    }, 1000)
    setTimeout(() => {
        subscribe.fire(10, 20, 30)
    }, 2000)

应用场景:凡是某个阶段到达的时候,需要执行很多方法(更多的时候,到底执行多少个方法不确定,需要边写业务,边处理),我们可以基于发布订阅模式来管理代码:

  • 创建事件池->发布计划
  • 向事件池中加入方法-->向计划表中订阅任务
  • fire-->通知计划表中的任务执行

2.一个项目中,我们可能会出现多个事情都需要基于发布订阅来管理,一个事件池不够

@1管理多个事件池

基于面向对象 类(subscribe,unsubscribe,fire公用的)&实例,(每个实例都有一个自己私有的事件池)

 class Sub {
        //实例私有的属性:私有的事件池
        pond = [];
        //原型上设置方法:向事件池中订阅任务
        subscribe(func) {
            let self = this,
                pond = self.pond;
            if (!pond.includes(func)) pond.push(func);
            //每一次执行,返回的方法是用来移除当前这个新增的这个方法的
            return function unsubscribe() {
                let i = 0,
                    len = pond.length,
                    item = null;
                for (; i < len; i++) {
                    item = pond[i];
                    if (item === func) {
                        pond.splice(i, 1)
                        break;
                    }
                }
            }
        };
        //通知当前实例所属事件池中的方法执行
        fire(...params) {
            let self = this,
                pond = self.pond;
            pond.forEach(item => {
                if (typeof item === 'function') {
                    item(...params)
                }
            })
        };

    }
    let sub1 = new Sub;
    sub1.subscribe(function () {
        console.log(1, arguments);
    })
    sub1.subscribe(function () {
        console.log(2, arguments);
    })
    setTimeout(() => {
        sub1.fire(100, 200)
    }, 1000)

    let sub2 = new Sub;
    sub2.subscribe(function () {
        console.log(1, arguments);
    })
    sub2.subscribe(function () {
        console.log(2, arguments);
    })
    setTimeout(() => {
        sub2.fire(200, 300)
    }, 2000)

@2一个事件池支持不同的事件池类型

 //type事件类型, func方法
    let sub = (function () {
        let pond = {};
        //向时间池中追加指定自定义事件类型的方法
        const on = function on(type, func) {
            //验证增加的时候,验证当前类型在事件池中是否已经存在
            !Array.isArray(pond[type]) ? pond[type] = [] : null;
            let arr = pond[type];
            if (arr.includes(func)) return;
            arr.push(func);
        };
        //从事件池中移除指定自定义事件类型的方法
        const off = function off(type, func) {
            let arr = pond[type],
                i = 0,
                item = null;
            if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
            for (; i < arr.length; i++) {
                item = arr[i];
                if (item == func) {
                    //移除掉
                    //splice改变原数组,导致数组塌陷(删除数组索引发生了变化,在循环会导致这个问题)
                    // arr.splice(i, 1)
                    arr[i] = null;//这样只是让集合中当前项值变为null,但是集合中的结构不会发生变化(索引不变),下次执行emit的时候,遇到当前项是null,我们在去把其移除掉即可
                    break;
                }
            }
        };
        //通知事件池中定自定义事件类型的方法执行
        const emit = function emit(type, ...params) {
            let arr = pond[type],
                i = 0,
                item = null;
            if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
            for (; i < arr.length; i++) {
                item = arr[i];
                if (typeof item === 'function') {
                    item(...params)
                    continue;
                }
                //不是函数把它移除掉即可
                arr.splice(i, 1);
                i--;
            }

        };

        return {
            on,
            off,
            emit
        };
    })()
    const fun1 = () => console.log(1)
    const fun2 = () => {
        console.log(2)
        sub.off('A', fun1)
    }
    const fun3 = () => console.log(3)
    const fun4 = () => console.log(4)
    const fun5 = () => console.log(5)
    const fun6 = () => console.log(6)

    sub.on('A', fun1);
    sub.on('A', fun2);
    sub.on('A', fun3);
    setTimeout(() => {
        sub.emit('A')
    }, 1000)


    sub.on('B', fun4);
    sub.on('B', fun5);
    sub.on('B', fun6);
    setTimeout(() => {
        sub.emit('B')
    }, 2000)
    setTimeout(() => {
        sub.emit('A')
    }, 3000)

观察者模式 

目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)

每一个观察者有update方法接收传的消息并处理

//定义观察者,形式可以不一样,只需要具备update方法
    class OB {
        update(msg) {
            console.log(`我是观察者1,我接受到的信息是${msg}`);
        }
    }
    let DEMO = {
        update(msg) {
            console.log(`我是观察者2,我接受到的信息是${msg}`);
        }
    };
    //目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)
    class Subject {
        observerList = [];
        add(observer) {
            this.observerList.push(observer)
        };
        remove(observer) {
            this.observerList = this.observerList.filter(item => item !== observer)
        };
        notify(...params) {
            this.observerList.forEach(item => {
                if (item && typeof item.update === "function") {
                    item.update(...params)
                }
            })
        }
    }
    let sub = new Subject;
    sub.add(new OB)
    sub.add(DEMO)
    setTimeout(() => {
        sub.notify('hello word')
    }, 1000);

中介者模式

    let mediator = (function () {
        let topics = [];
        const subscribe = function subscribe(callback) {
            topics.push(callback)
        };
        const publish = function publish(...params) {
            topics.forEach(callback => {
                if (typeof callback === 'function') {
                    callback(...params)
                }
            })
        };

        return {
            subscribe,
            publish
        }
    })()
    mediator.subscribe(() => console.log(1))
    mediator.subscribe(() => console.log(2))
    setTimeout(() => {
        mediator.publish()
    }, 1000)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JS设计模式 的相关文章

  • 不要模拟值对象:过于通用的规则,没有解释

    以下是 Mockito 单元测试框架的引用 不要模拟值对象 为什么有人会想要这样做呢 因为实例化对象太痛苦了 gt 无效 原因 如果创造新的装置太困难 那就是一个迹象 代码可能需要一些认真的重构 另一种方法是创建 价值对象的构建者 有一些工
  • TRACKER:错误TRK0005:无法找到:“CL.exe”。该系统找不到指定的文件

    我尝试在 Windows 8 上的 Node js 项目中执行以下命令 npm 安装 电子邮件受保护 cdn cgi l email protection 但我收到一个错误 我不知道如何处理 TRACKER 错误TRK0005 无法找到 C
  • 离子旋转器未显示

    我用 http 请求填充 Ionic 集合重复列表 但我不想将所有内容直接加载到 DOM 中 因此 我只显示其中一些项目 并在您向下滚动时添加其余项目 为此我实现了无限滚动功能 当我到达页面底部时 它应该显示一个旋转器 但它没有 这些物品至
  • 如何从 Google Custom Search API 获取超过 100 个结果

    我正在尝试使用 Google Custom Search API 在 Java 中进行研究 因此 我需要为每个查询提供一个大的结果集 然而 我似乎仅限于前 100 个结果 这比我需要的要少得多 我使用这样的列表方法 list setStar
  • 如何将函数传递给 JavaScript Web Worker

    我想通过一个 或多个 函数传递postMessage 给网络工作者 因为我无法引用常规文件 为了启动 Web Worker 我将一个对象 URL 从 Blob 创建 传递给Worker构造函数 然后我传递一条消息 但到目前为止还没有在消息中
  • 如何使用 Guava 连接字符串?

    我写了一些代码来连接字符串 String inputFile for String inputLine list inputFile inputLine trim 但我不能使用 连接 所以我决定使用 Guava 所以我需要使用Joiner
  • 更改时触发跨度文本/html

    jQuery 或 JavaScript 中是否有任何事件在以下情况下触发span标签 text html 已更改 Code span class user location span user location change functio
  • 删除 Laravel Mix 中的临时文件

    我想在 laravel mix 构建期间或之后删除临时构建文件 这是我目前拥有的一些代码 但是del不工作 const mix require laravel mix const del require del compile sass i
  • 在Java中多次读取System.in会导致IOException?

    我正在尝试创建一个小命令行游戏来强化我在过去几个月中在 Java 中学到的一些东西 我正在尝试创建一个名为 readInput 的方法 它返回一个我可以一次又一次调用的字符串 第一次它工作正常 但第二次它会导致 IO Exception 如
  • 无法在渲染器进程中使用 Node.js API

    无法在 Electron 中使用任何与 Electron 或节点相关的操作 未定义获取错误过程 我检查了他们指导添加节点支持的各个地方 但这已经完成了 所以卡在这里 我的主要应用程序代码是 const electron require el
  • 我们可以使用 for-each 循环来迭代 Iterator 类型的对象吗? [复制]

    这个问题在这里已经有答案了 如果我们执行以下操作 我们会收到错误 class FGH public static Iterator reverse List list Collections reverse list return list
  • 无法映射 ftl 文件中的 jsonRequest 属性

    我想在 FTL 文件中映射下面的 json 文件市场和子市场字段 但是当我尝试下面的代码时 它没有映射 有人可以帮助我吗 我从 2 天开始就无法映射它 Json请求 ProcessOrderRequest prevalidationMode
  • 找不到符号assertEquals

    我正在尝试为计算器编写第一个单元测试 但 NetBeans 说它找不到该符号assertEquals和注释 Test 我应该包括一些东西吗 我正在使用 NetBeans 7 3 1 和 W7 package calculator impor
  • 如何在 JavaScript 中对关联数组进行排序?

    我需要为我的一个项目通过 JS 对关联数组进行排序 我发现这个函数在 Firefox 中运行得很好 但不幸的是它在 IE8 OPERA CHROME 中不起作用 无法找到使其在其他浏览器中运行的方法 或者找到另一个适合该目的的函数 我真的很
  • `ie9` - contenteditable false 在父级可编辑时不起作用

    我正在尝试制作内容可编辑和不可编辑的容器 用户可以通过 3 种方式使用它 他们可以将内容与non editable 他们可以将内容与editable 他们可以在不选择其中之一的情况下放置内容 可编辑 我正在努力实现以下目标 content
  • Javascript:更改输入值时设置光标位置

    当您输入公式时 我试图在我的应用程序中重现类似于 Microsoft Excel Google Sheets 的用户体验 并且您可以使用不同的公式和变量来自动完成下拉菜单 为此 在验证自动完成功能后 我希望能够控制光标的位置 例如 如果我输
  • 如果抛出RuntimeException,是否可以将其作为异常捕获?

    如果我有一个try抛出一个块RuntimException子类 可以是后续的catch块将其捕获为Exception 具体来说 public class MyAppException extends RuntimeException In
  • 将带有 webapp 的 WAR 部署到 Maven 中央存储库是否有意义?

    这样做有意义吗 如果是 我在哪里可以找到使用简单的 Web Hello World 执行此操作的示例 当人们从 Maven 执行 Web 应用程序时 他们会使用 Jetty 来运行它吗 我想 tomcat 太重了 任何帮助将不胜感激 谢谢
  • 如何修复nodejs Express服务器中的“MulterError:意外字段”?

    我正在设置一个服务器来从客户端上传 zip 文件 服务器运行express和multer来执行此操作 上传文件时 服务器抛出 MulterError 意外字段 错误 我无法弄清楚是什么导致了它 我尝试过使用 png 图像 效果很好 但对于
  • Integer.parseInt 引发的 NumberFormatException

    嘿 我在学校上编码课 但老师没有很好地解释 所以我们必须在网上查找我所做的信息 但我无法找到代码中的错误 你能帮我吗 char end s do System out println Tipo de boleto char boleto c

随机推荐

  • 法如X330扫描仪在行业内的使用性

    3D扫描仪是一种用于捕捉实物三维模型的设备 通常通过激光 光线 摄像头等技术获取物体表面的点云数据 并将其转换成可编辑的三维模型 扫描仪的操作流程 1 准备工作 首先需要确定需要扫描的物体的大小 形状和材质 根据物体大小选择适当的扫描仪型号
  • 每天学命令

    report timing clock from edge from lead trail clock to clk signame list edge to lead trail rise fall early late hpin che
  • 【webrtc】音频采集-链接错误总结

    在集成webrtc的过程中 大量使用了directshow 大量的链接失败 而lib库又不好找 花费了大量时间 分享出来 共同学习 1 wmcodecdspuuid lib 1 gt audio device lib audio devic
  • 分析如何计算TVS管的功率?

    常见的汽车电源部分的原理图 分别是防反接模块和LDO模块 我们的客户要求ISO7637 2脉冲5为40V 400ms 内阻2欧姆 我开始时用的SMBJ20CA 结果TVS管被烧毁 后改成SMBJ36CA 现在可以过ISO7637 2脉冲5
  • TCP通信流程解析

    B S通信简述 整个计算机网络的实现体现为协议的实现 TCP IP协议是Internet的核心协议 HTTP协议是比TCP更高层次的应用层协议 HTTP HyperText Transfer Protocol 超文本传输协议 是互联网上应用
  • java变量的种类及作用域

    1 变量 在软件系统中 是将数据存储在内存之中的 而对内存中的数据的引用就是变量 可以理解为变量就是内存中数据的代词 简单说 变量就是指代在内存中开辟的存储空间 用于存放运算过程中需要用到的数据 代码如下所示 1 int a 5 2 int
  • 机器学习第九章树回归

    文章目录 引言 9 1复杂数据的局部性建模 9 2连续和离散型特征的树的构建 9 3将CART算法用于回归 9 3 1构建树 9 4树剪枝 9 4 1预剪枝 9 4 2后剪枝 9 5模型树 9 6小结 引言 上一章的线性回归包含了一些强大的
  • 安装程序无法验证产品密钥解决方案

    加载镜像 运行sources setup exe安装
  • dwz+struts+ajax,DWZ富客户端框架(dwzjs)结合struts2的增改删查

    DWZ是实用的国产JQuery UI框架 个人感觉比较好用 他和服务器端主要通过Ajax方式交互 数据格式为json 服务器响应数据代码示例 statusCode 200 message 操 DWZ是实用的国产JQuery UI框架 个人感
  • 【雕爷学编程】Arduino动手做(65)---TCRT5000红外寻迹传感器模块3

    37款传感器与模块的提法 在网络上广泛流传 其实Arduino能够兼容的传感器模块肯定是不止37种的 鉴于本人手头积累了一些传感器和执行器模块 依照实践出真知 一定要动手做 的理念 以学习和交流为目的 这里准备逐一动手试试多做实验 不管成功
  • layuiAdmin后台框架以及动态权限(二)

    之前写的ssm权限系统 不再赘述 由于之前的系统是由上到下 一层层查找封装的权限数据结构 系统性能不好 和数据库会有多次交互 下面介绍第二种方式 上一篇 layuiAdmin后台框架以及动态权限 源码杂录的博客 CSDN博客 layuiad
  • KORNIA与torch 版本存在依赖关系

    KORNIA 0 58对应torch 1 7 以上对应1 8 可以多下载几个多次安装 直到支持
  • html 引入md文件,webpack将打包目录下的md、html文件解析了

    webpack 加载不加载哪个文件 与你把文件放在哪个目录无关 与你设不设置 loader也无关 webpack 打包的时候会静态代码分析 从入口文件开始 把你require import的文件打包 比如 import xx from sr
  • SPSS-线性回归

    线性回归的因变量是连续数值型变量 回归的分类见113985634 R方 变量之间是否有相关性 模型汇总表 中R表示拟合优度 值越接近1表示模型越好 但不能说他们之间不相关 可能是非线性相关 一元线性回归里 相关系数平方就是R方 多元线性回归
  • 一个交期建议程序的坑 4gl SQL

    一个交期建议程序的坑 供应表已经包含了库存 替代料 需求表依然减掉了库存替代料 select 后的sum修改之后 忘记修改having的语句 差点搞死人 防不胜防 这里用到的算法 一张供应表 一张需求表 需求表不包含库存 在验量 替代量 在
  • 数据库连接运算(join)

    联接有三种 联接和自然联接 这里是算术比较符 外联接 1 联接 从R和S的笛卡儿乘积中选取满足条件 i j 的元组 2 自然联接 naturaljoin 两个关系R和S的自然联接操作具体计算过程如下 计算R S 设R和S的公共属性是A1 A
  • elasticsearch 安装配置

    20211208 es允许远程访问 在本地启动Elasticsearch后 发现只能用localhost和127 0 0 1访问 换成电脑的ip地址 显示拒绝访问 需要修改 config elasticsearch yml下的network
  • msvcr120.dll错误的解决方法

    msvcr120 dll错误 今天本来想操作mysql的 当我把那些文件配置好准备安装mysql时 bin gt mysqld install 居然报了个msvcr120 dll错误 就去寻找这个问题解决方法 原来是在C 资源库里少了一个资
  • Acwing 2. 01背包问题

    f i j 表示从前i个物品选 总体积 lt j的所有选法中的最大值 注意 当j
  • JS设计模式

    目录 前言 单例设计模式 Command 命令模式 Constructor构造器模式 工厂模式Factory 发布订阅设计模式 publish subscribe 观察者模式 中介者模式 前言 JS设计模式是一种思想 更规范更合理的管理代码