解决方案

JavaScript常见设计模式

seo靠我 2023-09-25 08:09:49

Javascript常⻅设计模式

设计模式总的来说是一个抽象的概念,是软件开发人员在开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当⻓的一段时间的试验和错误总结出来的。

1 、工SEO靠我厂模式

工厂模式是用来创建对象的一种最常用的设计模式。我们不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。

// 简单工厂模式:只需要一个正确的参数,就可以获取到SEO靠我你所需要的对象,用于生成实例 function Animal(opts) {var obj = new Object();obj.color = opts.color;obj.nameSEO靠我 = opts.name;obj.getInfo = function () {return "名称:" + obj.name + ", 颜色:" + obj.color;};return obj; SEO靠我 } var cat = Animal({ name: "波斯猫", color: "白色" }); // 工厂方法模式:本意是将实际创建对象的工作推迟到SEO靠我子类中,这样核心类就变成了抽象类, // 工厂方法只是一个实例化对象的工厂,只做实例化对象这一件事情,也是用于生成实例 class User {constructor(SEO靠我name = "", viewPage = []) {if (new.target === User) {throw new Error("抽象类不能实例化!");}this.name = name;SEO靠我this.viewPage = viewPage;} } class UserFactory extends User {constructor(name, viewPSEO靠我age) {super(name, viewPage); // 调用父类的constructor(name, viewPage),继承父类的this对象}create(role) {switch (rSEO靠我ole) {case "superAdmin":return new UserFactory("超级管理员", ["首⻚","通讯录","发现⻚","应用数据","权限管理",]);break;casSEO靠我e "admin":return new UserFactory("普通管理员", ["首⻚", "通讯录", "发现⻚"]);break;case "user":return new UserFacSEO靠我tory("普通用户", ["首⻚", "通讯录", "发现⻚"]);break;default:throw new Error("参数错误, 可选参数:superAdmin、admin、user")SEO靠我;}} } let userFactory = new UserFactory(); let superAdmin = userFactory.creaSEO靠我te("superAdmin"); let admin = userFactory.create("admin"); let user = userFactory.crSEO靠我eate("user"); //抽象工厂模式:并不直接生成实例,而是用于对产品类簇的创建 function getAbstractUserFactory(type) {SEO靠我switch (type) {case "wechat":return UserOfWechat;break;case "qq":return UserOfQq;break;case "weibo":SEO靠我return UserOfWeibo;break;default:throw new Error("参数错误, 可选参数:wechat、qq、weibo");} } lSEO靠我et WechatUserClass = getAbstractUserFactory("wechat"); let QqUserClass = getAbstractUserFactSEO靠我ory("qq"); let WeiboUserClass = getAbstractUserFactory("weibo"); let wechatUser = neSEO靠我w WechatUserClass("微信小李"); let qqUser = new QqUserClass("QQ小李"); let weiboUser = newSEO靠我 WeiboUserClass("微博小李");

在react开发中,通常在最外层组件传入不同的type来创建渲染不同样式的组件,就采用了工厂模式。通常用表驱动编程的实现来实现,比如

function tSEO靠我ranslate(type) {let terms = {1: <Com1/>,2: <Com2/>,3: <Com3/>}return terms[type]; }

2 、单例模式

保证SEO靠我一个类仅有一个实例,并提供一个访问它的全局访问点。

class Singleton {constructor() {} } Singleton.getInstance =SEO靠我 (function () {let instance;return function () {if (!instance) {instance = new Singleton();}return iSEO靠我nstance;}; })(); let s1 = Singleton.getInstance(); let s2 = Singleton.getInsSEO靠我tance(); console.log(s1 === s2); // true

在vuex源码中,通过一个外部变量来控制全局只有一个Vue实例:

let Vue; // bind on SEO靠我install export function install(_Vue) {if (Vue && _Vue === Vue) {// 如果发现 Vue 有值,就不重新创建实例了retSEO靠我urn;}Vue = _Vue;applyMixin(Vue); }

在不使用mongoose时,对数据库的操作进行封装:

// 封装db类库 const { MongoCSEO靠我lient } = require(mongodb) const { dbUrl, dbName } = require(./config)class Db {static getInSEO靠我stance() {// 为了实现单例模式(多次实例化实例不共享的问题)if (!Db.instance) {Db.instance = new Db()}return Db.instance}conSEO靠我structor() {this.dbClient = ;/*构造函数中存属性放db对象(解决数据库多次连接问题而提升效率)*/this.connect()}connect() {/*连接数据库*/rSEO靠我eturn new Promise((resolve, reject) => {if (!this.dbClient) {MongoClient.connect(dbUrl, { useUnifiedSEO靠我Topology: true }, (err, client) => {if (err) {reject(err);return;}const db = client.db(dbName)this.dSEO靠我bClient = db// 存放在构造函数中(为了解决数据库多次连接问题)resolve(this.dbClient)})} else {// 如果构造函数中有dbClient对象resolve(tSEO靠我his.dbClient)}})}find(collectionName, json) {return new Promise((resolve, reject) => {this.connect()SEO靠我.then((db) => {const result = db.collection(collectionName).find(json)result.toArray((err, docs) => SEO靠我{if (err) {reject(err);return;}resolve(docs)})})})} }module.exports = Db.getInstance()

3 、适配器SEO靠我模式

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。

// 已有的地图接口 SEO靠我 var googleMap = {show: function () {console.log("开始渲染谷歌地图");}, }; var baiduMapSEO靠我 = {display: function () {console.log("开始渲染百度地图");}, }; // 已有的渲染接口 var rendeSEO靠我rMap = function (map) {if (map.show instanceof Function) {map.show();} }; // 目的 SEO靠我 renderMap(googleMap); // 开始渲染谷歌地图 renderMap(baiduMap); // 无效 // 适配器 var SEO靠我baiduMapAdapter = {show: function () {return baiduMap.display();}, }; renderMap(googSEO靠我leMap); // 开始渲染谷歌地图 renderMap(baiduMapAdapter); // 开始渲染百度地图

4 、装饰模式

装饰模式不需要改变已有的接口,给对象添加额外的功能。SEO靠我比如使用ES 7 中的装饰器语法:

function readonly(target, key, descriptor) {descriptor.writable = false;return descSEO靠我riptor; } class Test {@readonlyname = "yck"; } let t = new Test(); SEO靠我 t.yck = "111"; //不可改变

在redux中就使用connect修饰器对我们需要使用全局状态的组件进行修饰(类似高阶组件)

在antd3的form表单中也是使用到了修饰器来对组SEO靠我件注入antd表单的功能(现在都用useForm)

5 、代理模式

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在两个对象之间起到中介的作用。(个人理解:代理就是中间人通信

clSEO靠我ass Car {drive() {return "driving";} } class Driver {constructor(age) {this.age = agSEO靠我e;} } class CarProxy {constructor(driver) {this.driver = driver;}drive() {return thiSEO靠我s.driver.age < 18 ? "too young to drive" : new Car().drive();} } const driver = new SEO靠我Driver(18); const carProxy = new CarProxy(driver).drive(); // driving

此外,还有es 6 proxy,也就是在目标对SEO靠我象之前架设一层拦截,或者叫代理:

var obj = {}; var proxy = new Proxy(obj, {get: function (target, key, receivSEO靠我er) {console.log(`getting ${key}!`);return Reflect.get(target, key, receiver);},set: function (targeSEO靠我t, key, value, receiver) {console.log(`setting ${key}!`);return Reflect.set(target, key, value, receSEO靠我iver);}, }); proxy.count = 1; // setting count! ++proxy.count; SEO靠我 // getting count! // setting count! // 2 console.log(obj.count); // 2

在前端开SEO靠我发中在本地起开发服务器,然后使用代理进行转发请求后端服务器的方式,实现解决浏览器的跨域问题。(浏览器先请求本地服务器->本地服务器再请求后端服务器->后端服务器返回数据->本地服务器接收数据->本地服SEO靠我务器返给浏览器->实现跨域)也是借用代理的实现。

6 、观察者模式

定义了对象间一对多的依赖关系,当目标对象的状态发生改变时,所有依赖它的对象都会得到通知。

// 目标者类 class SuSEO靠我bject {constructor() {this.observers = []; // 观察者列表}// 添加add(observer) {this.observers.push(observerSEO靠我);}// 删除remove(observer) {let idx = this.observers.findIndex((item) => item === observer);idx > -1 &SEO靠我& this.observers.splice(idx, 1);}// 通知notify() {for (let observer of this.observers) {observer.updatSEO靠我e();}} } // 观察者类 class Observer {constructor(name) {this.name = name;}// 目标对SEO靠我象更新时触发的回调update() {console.log(`目标者通知我更新了,我是:${this.name}`);} } // 实例化目标者 leSEO靠我t subject = new Subject(); // 实例化两个观察者 let obs1 = new Observer("前端开发者"); letSEO靠我 obs2 = new Observer("后端开发者"); // 向目标者添加观察者 subject.add(obs1); subject.add(oSEO靠我bs2); // 目标者通知更新 subject.notify(); // 输出: // 目标者通知我更新了,我是前端开发者 SEO靠我 // 目标者通知我更新了,我是后端开发者

vue源码中的依赖管理和通知更新就是基于观察者模式。

再比如github仓库也是使用了观察者模式,远程仓库提交变化后,会通知所有的观察者需要拉取代码了。

7 SEO靠我、发布订阅模式

实现了对象间多对多的依赖关系,通过事件中心管理多个事件。目标对象并不直接通知观察者,而是通过事件中心来派发通知。

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

观察者SEO靠我模式中观察者和目标对象是存在关联的。(目标对象可随时广播通知所有观察者并触发各自的事件,必须订阅才会被通知)发布订阅模式中订阅者和发布者没有关联。(通过事件中心管理,可以只订阅不发布,也可以只发布不订SEO靠我阅,也可以即发布也订阅) // 事件中心 let pubSub = {list: {}, // {onwork:[fn1,fn2],offwork:[fn1,fn2],SEO靠我launch:[fn1,fn2]}subscribe: function (key, fn) {// 订阅if (!this.list[key]) {this.list[key] = [];}thisSEO靠我.list[key].push(fn);},publish: function (key, ...arg) {// 发布for (let fn of this.list[key]) {fn.call(SEO靠我this, ...arg);}},unSubscribe: function (key, fn) {// 取消订阅let fnList = this.list[key];if (!fnList) reSEO靠我turn false;if (!fn) {// 不传入指定取消的订阅方法,则清空所有key下的订阅fnList && (fnList.length = 0);} else {fnList.forEacSEO靠我h((item, index) => {if (item === fn) {fnList.splice(index, 1);}});}}, }; // 订阅 SEO靠我 pubSub.subscribe("onwork", (time) => {console.log(`上班了:${time}`); }); pubSub.subsSEO靠我cribe("offwork", (time) => {console.log(`下班了:${time}`); }); pubSub.subscribe("launchSEO靠我", (time) => {console.log(`吃饭了:${time}`); }); // 发布 pubSub.publish("offwork"SEO靠我, "18:00:00"); pubSub.publish("launch", "12:00:00"); // 取消订阅 pubSub.unSubscrSEO靠我ibe("onwork");

参考链接:

https://juejin.cn/post/6844903653774458888#heading- 3

https://refactoringguru.cn/dSEO靠我esign-patterns/catalog

http://interview.poetries.top/docs/excellent.html#_34-设计模式

参考书籍:《JavaScript设计模式SEO靠我与开发实践》

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2