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设计模式 的相关文章

  • 使用java将文件从GCS存储桶传输到SFTP服务器

    我能够从 GCS 存储桶读取文件 但所有库都喜欢jsch将文件传输到 SFTP 服务器时会查找文件路径 而不是内存中的文件 我不想将从GCS读取的文件写入磁盘 如何将内存中的文件传输到SFTP 我假设您想上传内存中的数据 JSch 实际上有
  • Java 会话变量

    我听说有些人认为在会话中将信息存储在服务器上是一个坏主意 因为它不安全 因此 在多页面业务流程功能中 应用程序将数据写入数据库 然后在需要时检索信息 在会话中存储私人信息是否一定不安全 只要会话本身安全 在会话中存储属性就不存在安全风险劫持
  • 在 Scala 中创建 Java 对象

    我有一个 Java 类 Listings 我在 Java MapReduce 作业中使用它 如下所示 public void map Object key Text value Context context throws IOExcept
  • chrome.extension.getBackgroundPage() 函数示例

    我正在开发一个需要在后台运行的小型 Chrome 扩展 但是 我知道当我使用弹出窗口时这是不可能的 经过一番阅读后 似乎最好的选择是创建popup js为了运行background js using chrome extension get
  • Phantomjs page.content 未检索页面内容

    我使用 Phantomjs 来抓取使用 JavaScript 和 Ajax 加载动态内容的网站 我有以下代码 var page require webpage create page onError function msg trace v
  • Javascript TypeError:无法读取未定义的属性“indexOf”

    在此代码中 我想从cart products array var cart products 17 1 19 1 18 1 var product 17 each cart products function key item if ite
  • 在 GeoJson 数据接收到的 Google 地图多边形上放置标签

    我想将带有信息的标签 或带有标签的 div 放在由下面的代码片段绘制的多边形上 样式属性已成功应用于要素 多边形类型 有谁知道如何向该特征添加文本并将其显示在多边形的中心 function handleGeoJson data map da
  • 如何修复 java.lang.ClassNotFoundException: org.springframework.boot.configurationprocessor.json.JSONException 错误?

    当我在生产环境中将 Spring Boot 服务作为 Windows 服务运行时 出现以下错误 服务exe的创建者是Jar2exe https www jar2exe com java lang reflect InvocationTarg
  • 如何使用nodejs获取目录中第一个扩展名为.txt的文件?

    我所有文件所在的目录是 usr home jordan 该目录下有很多文件 在目录本身中 但有一个文件以 txt 扩展名命名 使用nodejs和fs 我想将带有txt扩展名的第一个文件 或任何文件 放入 mytxtfilepath 中 我在
  • 清单合并失败:需要为 显式指定 android:exported

    我的清单文件有问题 错误消息 清单合并失败 android 需要为 明确指定导出 面向 Android 12 及更高版本的应用需要指定显式值android exported当相应的组件定义了意图过滤器时 有关详细信息 请参阅 https d
  • Angular 中的动态子组件

    我正在构建一个具有一致的元素列表设计模式的应用程序 如果我有一个 A 类型的对象 我会创建AComponent它接受a作为输入 然后创建另一个组件来迭代 A 列表 AListComponent 那么如果我有一个对象 B 我需要做同样的事情
  • 我们可以将请求分派到 servlet 内的 HTML

    这可能吗 RequestDispatcher rd request getRequestDispatcher index html rd forward request response 是的 您可以将请求分派到 HTML 页面
  • 使用用户名和密码登录 LinkedIn 失败

    LinkedIn使用oauth登录其api 服务器中无法登录api 我尝试使用http请求登录linkedin并获取oauth verifier 但我得到了这样的回应 很抱歉 出现了问题 你的申请 请确保您 启用cookie并重试 或点击此
  • Selenium 查看鼠标/指针

    有什么方法可以在运行测试时真正看到硒鼠标吗 要么是 Windows 光标图像 要么是某种点或十字线或任何东西 我正在尝试使用拖放功能selenium and java in an HTML5Web 应用程序 并且能够看到光标以了解它实际在做
  • 为什么我得到:没有有效的 JFX 运行时

    我有一个使用 java 1 6 编译并使用 jnlp webstart 运行的现有应用程序 如果我使用 JRE 1 6 从客户端运行此应用程序 一切都会很好 但是 当我使用 java JDK 7 编译代码并使用 JRE 1 7 67 运行客
  • iOS Javascript 引擎 parseFloat(1) 返回负数

    这段代码将使错误出现 function causeBug d var k var n parseFloat 1 var c Math abs d if n lt 0 k else k return k n function for var
  • 如何在 Hibernate 中使用 SELECT 进行 INSERT

    我需要在休眠中实现以下请求 insert into my table max column values select max id from special table where 如何在休眠中使用注释来做到这一点 Special tab
  • Mac 上的 JavaFX WebView 字体问题

    有些网站显示乱码而不是正确的文本 它只发生在 Mac 上 For example with GMapsFX 可能与 OS X 10 11 或 10 12 有关 我用Java 1 8 0 121测试了它 此问题有任何修复或解决方法吗 就我而言
  • AES 在 cryptojs 中加密并在 python Crypto.Cipher 中解密

    使用 js CryptoJS 加密并使用 python crypto Cipher 解密时出现问题 这是我在js中的实现 附加 iv 与加密消息并使用 base64 进行编码
  • 如何使用javascript取消设置变量? [复制]

    这个问题在这里已经有答案了 这是我到目前为止所尝试的 var nxt I am next window onscroll function var scr this pageYOffset if scr gt 400 console log

随机推荐

  • 法如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设计模式是一种思想 更规范更合理的管理代码