js设计模式实例

js设计模式

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
而虽然js前端使用设计模式不像后端那么频繁,但是依然是一名程序员必备的本领,本文通过使用实例方便大家理解记忆设计模式,可用于面试前速记。
一. 设计模式介绍
在js设计模式中,核心:封装变化。
将变与不变分离,确保变化灵活,不变稳定。

  1. 构造器原型模式
    使用设计模式前:
 var employee1 = {name:"zs",age:18}var employee2 = {name:"ls",age:19}

在构造对象时,如果数据量变多,对象的代码会变得冗余臃肿
使用设计模式后:

function Employee(name,age){this.name = name;this.age =age;this.say = function(){console.log(this.name+"-",this.age)}
}
new Employee("zs",18)
new Employee("ls",19)
  1. 工厂模式
    由一个工厂对象决定创建某一种产品的对象类的实例。主要用来创建同一类对象。
    只需要传入参数,就能返回具体的实例。
function UserFactory(role){function User(role,pages){this.role = role;this.pages = pages;}switch(role){case "superadmin":return new User("superadmin",["home","user-manage","right-manage","news-manage"])break;case "admin":return new User("admin",["home","user-manage","news-manage"])break;case "editor":return new User("editor",["home","news-manage"])break;default:throw new Error('参数错误')}
}
  1. 抽象工厂模式
    相比于工厂模式,抽象工厂模式会抽离出一个不变的公共父类。其子类就是各种的不同类。再通过函数判断返回这写不同的类。它并不会直接生成可用实例。而是返回不同的类,让开发者得到想要的类后自行实例化。
class User {constructor(name) {this.name = name}welcome() {console.log("欢迎回来", this.name)}dataShow() {throw new Error("抽象方法不允许直接调用")}
}class Editor extends User {constructor(name) {super(name)this.role = "editor"this.pages = ["home", "news-manage"]}dataShow() {console.log("editor的可视化逻辑")}
}class Admin extends User {constructor(name) {super(name)this.role = "admin"this.pages = ["home", "user-manage", "news-manage"]}dataShow() {console.log("admin的可视化逻辑")}AddUser() {console.log("adduser方法")}
}class SuperAdmin extends User {constructor(name) {super(name)this.role = "superadmin"this.pages = ["home", "user-manage", "right-manage", "news-manage"]}dataShow() {console.log("superadmin的可视化逻辑")}AddUser() {console.log("adduser方法")}AddRight() {console.log("addright方法")}
}function getAbstractUserFactory(role) {switch (role) {case 'superadmin':return SuperAdmin;break;case 'admin':return Admin;break;case 'editor':return Editor;break;default:throw new Error('参数错误')}
}
  1. 建造者模式:
    假如你是一个工厂的设计师,同过前面的工厂或抽象工厂模式,已经获得了一个个独立的零件。接下来要拼接在一起成一个新的大零件。你现在已经不需要关心小零件构造,只需要关心大零件的组成结构与顺序。这就是建造者模式!
class Navbar {//没一个类的细节都不同init() {console.log("navbar-init")}getData() {return new Promise((resolve) => {setTimeout(() => {resolve()console.log("navbar-getData")}, 1000)})}render() {console.log("navbar-render")}
}
class List {init() {console.log("List-init")}getData() {return new Promise((resolve) => {setTimeout(() => {resolve()console.log("List-getData")}, 1000)})}render() {console.log("List-render")}
}class Operator {async startBuild(builder) {//设计师只关心这个函数中代码的运行逻辑await builder.init()await builder.getData()await builder.render()}
}const op = new Operator();
const navbar = new Navbar();
const list = new List();
op.startBuild(navbar);
op.startBuild(list); 
  1. 单例模式
    作为设计师的你,有一个工具包,工具包里有且只有一个趁手的工具。你每次都只会使用这个工具。然而一开始你并没有工具包,你可以买一个并一直使用这一个
    为防止全局污染可以使用闭包来封闭一个变量,这就是单例模式的原理。
    保证一个类仅有一个实例,并提供一个访问它的全局访问点。
    主要解决一个全局使用的类频繁地创建和销毁,占用内存。
    const Modal = (function () {let modal = nullreturn function () {//返回一个类,但是由于modal变量访问了外层作用域所以缓存不会立即释放if (!modal) {modal = document.createElement('div')modal.innerHTML = '登录对话框'modal.className = 'kerwin-modal'modal.style.display = 'none'document.body.appendChild(modal)}return modal}})()document.querySelector('#open').addEventListener('click', function () {const modal = new Modal()modal.style.display = 'block'})document.querySelector('#close').addEventListener('click', function () {const modal = new Modal()modal.style.display = 'none'})
  1. 装饰器模式
    假设工厂中生产的是一台电视,在设计电视时,总会有开机广告,这是固定好的但又跟电视本身的内容无关联,那么开机广告就是有前置装饰器设置的。
    对已有功能进行拓展,这样不会更改原有代码,对其他的业务产生影响,这方便我们在较少的改动下对软件进行拓展。
 Function.prototype.before = function (beforeFn) {var _this = this;return function () {beforeFn.apply(this, arguments);return _this.apply(this, arguments);};};
Function.prototype.after = function (afterFn) {var _this = this;//函数体本省return function () {//返回一个新的函数var ret = _this.apply(this, arguments);afterFn.apply(this, arguments);return ret;};
};function test() {console.log("11111")
}
var test1 = test.before(() => {console.log("00000")
}).after(()=>{console.log("22222")
})test1()
  1. 适配模式
    工厂中的水管并不是大小一样的,这时候使用一个首尾大小不一样的(适配器)工具可以解决此问题
    可以理解为类中的函数柯里化
    将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作,比如在原本的函数外再嵌一个函数,这样调用函数名变了但我们使用适配器改变了原有的函数名变为了调用的函数名(这在以下项目中更改接口api很有用!)
 //按照官网代码复制
class TencentMap {show() {console.log('开始渲染腾讯地图');}
}
//按照官网代码复制
class BaiduMap {display() {console.log('开始渲染百度地图');}
}class BaiduMapAdapter extends BaiduMap {constructor() {super();}render() {this.display();}
}
class TencentMapAdapter extends TencentMap {constructor() {super();}render() {this.show();}
}
// 外部调用者
function renderMap(map) {map.render(); // 统一接口调用
}
renderMap(new TencentMapAdapter());
renderMap(new BaiduMapAdapter());
  1. 策略模式
    假如你作为设计师处理一件很复杂的事情,你可以选择一个人扛,也可以选择采用身边助理的意见。策略模式就是如此。将if else分开放到对象中去。这样想要加逻辑时就不必关注if else的先后顺序。
        var list = [{title: "男人看了沉默",type: 1},{title: "震惊",type: 2},{title: "kerwin来了",type: 3},{title: "tiechui离开了",type: 2}]let obj = {1: {content: "审核中",className: "yellowitem"},2: {content: "已通过",className: "greenitem"},3: {content: "被驳回",className: "reditem"}}mylist.innerHTML = list.map(item =>`<li><div>${item.title}</div>    <div class="${obj[item.type].className}">${obj[item.type].content}</div>    </li>`).join("")
  1. 代理模式
    代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
    代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。
    准确来说就好似Proxy进行代理,通过其代理功能可以过滤掉一些禁用。缺点是必须访问代理后的对象,不会影响原有对象。vue3响应式底层原理
let obj = {}
let proxy = new Proxy(obj,{get(target,key){console.log("get",target[key])return target[key]},set(target,key,value){console.log("set",target,key,value)if(key==="data"){box.innerHTML = value}target[key] = value}
})
  1. 观察者模式
    作为总设计师,会有许多手下,他们都是看你办事,你要是发出号令,他们就得相应做出变化
    一个被观察对象有许多观察者对象,被观察者变化,观察者发生相应变化。
class Sub {constructor() {this.observers = []}add(observer) {this.observers.push(observer)}remove(observer) {this.observers = this.observers.filter(item => item !== observer)}notify() {this.observers.forEach(item => item.update())}
}class Observer {constructor(name) {this.name = name}update() {console.log("通知了", this.name)}
}
const observer1 = new Observer("kerwin")
const observer2 = new Observer("tiechui")const sub = new Sub()
sub.add(observer1)
sub.add(observer2)setTimeout(() => {sub.notify()
}, 2000)
  1. 发布订阅模式
    虽然作为总设计师有很大权利,但总是有上级的,上级有时会发布一些任务下来,这些任务有的不是立即完成,你可以记录下来。到了时候再完成即可。
    观察者模式:观察者和目标要相互知道
    发布订阅模式:发布者和订阅者不用互相知道,通过第三方实现调度,属于经过解耦合的观察者模式
 class SubPub {constructor() {this.message = {}//记录本}subscribe(type, fn) {if (!this.message[type]) {this.message[type] = [fn]} else {this.message[type].push(fn)}}publish(type, ...arg) {if (!this.message[type]) returnconst event = {type: type,arg: arg || {}}// 循环执行为当前事件类型订阅的所有事件处理函数this.message[type].forEach(item => {item.call(this, event.arg)})}}

与观者不同的是,订阅的是事件而非对象,而且有不同的记录本,记录不同的事
14. 模块化模式
模块化模式最初被定义为在传统软件工程中为类提供私有和公共封装的一种方法。

能够使一个单独的对象拥有公共/私有的方法和变量,从而屏蔽来自全局作用域的特殊部分。这可以减少我们的函数名与在页面中其他脚本区域内定义的函数名冲突的可能性。
早前模块化是由闭包完成

var testModule = (function () {var count = 0;return {increment () {return ++count;},reset: function () {count = 0;},decrement(){return --count;}};})();

模块化

export default {name:"moduleA",test(){return "test"}
}<script type="module">import moduleA from './1.js'console.log(moduleA)
</script>
  1. 迭代器模式
    为遍历不同数据结构的 “集合” 提供统一的接口;
    能遍历访问 “集合” 数据中的项,不关心项的数据结构
// 统一遍历接口实现
var kerwinEach = function (arr, callBack) {for (let i = 0; i < arr.length; i++) {callBack(i, arr[i])}
}// 外部调用
kerwinEach([11, 22, 33, 44, 55], function (index, value) {console.log([index, value]);var oli  =document.createElement("li")oli.innerHTML = valuelist.appendChild(oli)
})
  1. 指责链模式
    Promise的then链式调用采用的就是这种模式
    使多个对象都有机会处理请求,从而避免了请求的发送者与多个接收者直接的耦合关系,将这些接收者连接成一条链,顺着这条链传递该请求,直到找到能处理该请求的对象。
btn.addEventListener("click", function (event) {checks.check()
});function checkEmpty() {if (input.value.length == 0) {console.log("这里不能为空");return}return "next"
}function checkNumber() {if (Number.isNaN(+input.value)) {console.log("这里必须是数字");return}return "next"
}function checkLength() {if (input.value.length < 6) {console.log("这里要大于6个数字");return}return "next"
}class Chain {constructor(fn) {this.checkRule = fn || (() => "next");this.nextRule = null;}addRule(nextRule) {this.nextRule = new Chain(nextRule);return this.nextRule;//不断返回实例,实现链式调用}end() {this.nextRule = {check: () => "end"};}check() {this.checkRule() == "next" ? this.nextRule.check() : null;}
}const checks = new Chain();
checks.addRule(checkEmpty).addRule(checkNumber).addRule(checkLength).end();

最后还有一种不算设计模式但是非常有用的模式,名为函数柯里化
试想一下你要打印一个日志,它的内容是时间,类型,以及内容
很明显时间在一天中日期是不会变的,类型也就那么几种,唯独内容每次都不一样
但是前面两个参数,只是不会频繁变化,但却不能完全写死。
这时需要一个容器,记住前面两个参数。大家可能会想到使用Object对象,但是不要忘了函数也是对象,合理利用函数闭包,完全可以优雅的解决这个棘手的问题。

function logs(x){return function(y){return function(z){console.log(`时间:${x},类型:${y},内容:${z}`)}}
}
let logDate=logs(new Date().toLocaleDateString())//这个容器保存当天时间
let logDateError=logDate('error')//定义不同类型
let logDateSuccess=logDate('success')
let message1=logDateError('错误信息1')//填写完全不同的内容
let message1=logDateError('错误信息2')
let message2=logDateSuccess('成功信息1')

这样写logs函数大家可能担心陷入回调地狱,凡是关于回调地狱都可以使用递归解决!
自动封装函数:

function currying(fn) {let curryFn = function (...args) {if (args.length >= fn.length) {return fn(...args)} else {return function (...newArgs) {return curryFn(...args, ...newArgs)}}}return curryFn
}function test(a, b, c) {console.log(a, b, c)
}
var testCurrying = currying(test)//放入该函数即可自动生成
testCurrying(1)(2)(3)

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

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

相关文章

Linux命令-arj命令(用于创建和管理.arj压缩包)

补充说明 arj命令 是 .arj 格式的压缩文件的管理器&#xff0c;用于创建和管理 .arj 压缩包。 语法 arj(参数)参数 操作指令&#xff1a;对 .arj 压缩包执行的操作指令压缩包名称&#xff1a;指定要操作的arj压缩包名称

应急响应红蓝工程师白帽子取证Linux和windows入侵排查还原攻击痕迹,追溯攻击者,以及各种木马和病毒以及恶意脚本文件排查和清除

应急响应红蓝工程师白帽子取证Linux入侵排查还原攻击痕迹,追溯攻击者,以及各种木马和病毒以及恶意脚本文件排查和清除。 一般服务器被入侵的迹象,包括但不局限于:由内向外发送大量数据包(DDOS肉鸡)、服务器资源被耗尽(挖矿程序)、不正常的端口连接(反向shell等)、服务…

Go 知识chan

Go 知识chan 1. 基本知识1.1 定义1.2 操作1.3 操作限定1.4 chan 读写 2. 原理2.1 数据结构2.2 环形队列2.3 等待队列2.4 类型消息2.5 读写数据2.6 关闭chan 3. 使用3.1 操作符使用3.2 select3.3 for-range https://a18792721831.github.io/ 1. 基本知识 chan是go里面里面提供…

源码篇--Redis 五种数据类型

文章目录 前言一、 字符串类型&#xff1a;1.1 字符串的编码格式&#xff1a;1.1.1 raw 编码格式:1.1.2 empstr编码格式:1.1.3 int 编码格式:1.1.4 字符串存储结构展示: 二、 list类型&#xff1a;2.1 List 底层数据支持&#xff1a;2.2 List 源码实现&#xff1a;2.3 List 结构…

微信小程序-04

rpx&#xff08;responsive pixel&#xff09;是微信小程序独有的&#xff0c;用来解决屏适配的尺寸单位。 import 后跟需要导入的外联样式表的相对路径&#xff0c;用 ; 表示语句结束。 定义在 app.wxss 中的样式为全局样式&#xff0c;作用于每一个页面。 在页面的 .wxss 文…

独享http代理安全性是更高的吗?

不同于共享代理&#xff0c;独享代理IP为单一用户提供专用的IP&#xff0c;带来了一系列需要考虑的问题。今天我们就一起来看看独享代理IP的优势&#xff0c;到底在哪里。 我们得先来看看什么是代理IP。简单来说&#xff0c;代理服务器充当客户机和互联网之间的中间人。当你使用…

Android拍照的方式:调用系统相机、自定义 Camera

一、调用系统相机&#xff1a; 清单文件配置授权、打开相机获取照片注意事项 1、清单文件配置 A、配置使用相机权限 <uses-featureandroid:name"android.hardware.camera"android:required"false" /><uses-permission android:name"andro…

自动驾驶代客泊车AVP泊车规划模块详细设计

目录 1. 背景... 3 2. 泊车模块架构图... 4 3. 泊车规划模块信号... 5 3.1 输入信号... 5 3.2 输出信号... 5 4. 模块预处理... 6 4.1 坐标系转换... 6 4.2 车库及轨迹处理策略... 7 5. 泊车规划模块算法设计... 8 5.1 泊入规划模块... 8 5.1.1 泊入…

幻兽帕鲁一键开私服?超简单小白教程一看就会!

如何自建幻兽帕鲁服务器&#xff1f;基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了&#xff0c;一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程…

Python学习之路-Django基础:PythonWeb

Python学习之路-Django基础:PythonWeb Python Web 框架要点 处理流程 图片来源于未来的小牛的CSDN博客 意义 用于搭建Web应用程序&#xff0c;免去不同Web应用相同代码部分的重复编写&#xff0c;只需关心Web应用核心的业务逻辑实现 Web应用程序的本质 接收并解析HTTP请求…

HCIA学习作业三

要求&#xff1a; 拓扑图&#xff1a; <AR1>ping 5.5.5.1 <AR1>display ip interface brief <AR1>display ip routing-table <AR1>display ip routing-table protocol static <AR2>ping 5.5.5.1 <AR2>display ip interface brief <…

Linux实现:从倒计时到进度条

文章目录 1.回车与换行2.缓冲区的概念3.倒计时4.进度条(第一版无应用场景)5.进度条(第二版有应用场景) 1.回车与换行 2.缓冲区的概念 强制刷新可以使用冲刷函数fflush #include <stdio.h> #include <unistd.h> int main() {printf("I am a \nhandsome man!&q…

排序(1)——直接插入排序、希尔排序

目录 一、直接插入排序 1.简介 2.思路与代码 3.复杂度与稳定性分析 &#xff08;1&#xff09;时间复杂度 &#xff08;2&#xff09;空间复杂度 &#xff08;3&#xff09;稳定性 二、希尔排序 1.简介 2.思路与代码 &#xff08;1&#xff09;分组排序 &#xff08…

系统架构设计师教程(十七)通信系统架构设计理论与实践

通信系统架构设计理论与实践 17.1 通信系统概述17.2 通信系统网络架构17.2.1局域网网络架构17.2.2 广域网网络架构17.2.3 移动通信网网络架构17.2.4存储网络架构17.2.5 软件定义网络架构17.3 网络构建关键技术17.3.1 网络高可用设计17.3.2 IPv4与IPv6融合组网技术17.3.3 SDN技术…

09. Springboot集成sse服务端推流

目录 1、前言 2、什么是SSE 2.1、技术原理 2.2、SSE和WebSocket 2.2.1、SSE (Server-Sent Events) 2.2.2、WebSocket 2.2.3、选择 SSE 还是 WebSocket&#xff1f; 3、Springboot快速集成 3.1、添加依赖 3.2、创建SSE控制器 3.2.1、SSEmitter创建实例 3.2.2、SSEmi…

macOS跨进程通信: Unix Domain Socket 创建实例

macOS跨进程通信: Unix Domain Socket 创建实例 一&#xff1a; 简介 Socket 是 网络传输的抽象概念。 一般我们常用的有Tcp Socket和 UDP Scoket&#xff0c; 和类Unix 系统&#xff08;包括Mac&#xff09;独有的 Unix Domain Socket&#xff08;UDX&#xff09;。 Tcp So…

Scratch:启蒙少儿编程的图形化魔法

在当今这个数字化时代&#xff0c;编程已经成为了一项重要的基础技能。就像学习阅读和写作一样&#xff0c;掌握编程能够打开通往未来世界的大门。对于孩子们来说&#xff0c;Scratch作为一种图形化编程语言&#xff0c;不仅简单有趣&#xff0c;而且非常适合作为编程学习的入门…

科技助力“双碳”:墨水屏电子桌牌在绿色办公中的作用

随着科技的发展&#xff0c;人们对绿色环境可持续发展也越来越重视&#xff0c;所以&#xff0c;我国在几年前&#xff0c;就提出了“双碳”政策&#xff08;即碳达峰与碳中和的简称。2020年9月中国明确提出2030年“碳达峰”与2060年“碳中和”目标&#xff09;&#xff0c;而作…

单调栈第二天(还没写完)

503.下一个更大元素II 力扣题目链接(opens new window) 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序&#xff0c;这个数字之后的第一个比它更…

卸载软件Geek Uninstaller,MySQl安装不成功

最近刷最右的时候&#xff0c;看到两个帖子都是MySQl安装过程总是出现问题。大概两年前我也遇到了这个问题&#xff0c;推荐一款软件。 是因为在安装的过程之中&#xff0c;出现了问题。然后你再进行安装的时候&#xff0c;没有完全将原来安装的软件卸载掉&#xff0c;导致有注…