创建型模式
工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,它提供了一种创建对象的最佳方式。
个人理解类似于工厂车间,可以生产指定产品
function函数示例:
function factoryMode(name, age) { const obj = { name, age }; return obj; } const zs = factoryMode('张三', 18); const ls = factoryMode('李四', 20) 复制代码
构造函数模式
调用构造函数 new fn() ,返回拥有指定特征的对象实例
和工厂模式有相同之处,不同如下:
1.没有显式的创建对象
2.直接将属性和方法赋给了this对象
3.没有return语句
new 做了以下4个步骤
1.创建一个新的对象
2.将构造函数的作用域付给新对象(因此this就指向了这个新对象)
3.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象
function函数示例:
function ConstructorMode(name, age) { this.name = name; this.age = age; }; const zs = new ConstructorMode('张三', 18); const ls = new ConstructorMode('李四', 20) 复制代码
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。它提供了一种创建对象的最佳方式。
将公共的方法,属性放的原型(prototype)上,避免重复定义,便于维护
function PrototypeMode(name, age) { this.name = name; this.age = age; } PrototypeMode.prototype.sayName = function () { console.log('my name is', this.name); } const zs = new PrototypeMode('张三', 18); const ls = new PrototypeMode('李四', 20); zs.sayName(); ls.sayName(); 复制代码
单例模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象,它提供了一种创建对象的最佳方式。
funciton函数示例:
function singletonMode(name, age) { let instance = {}; return function (name, age) { if (!instance.name) { instance.name = name; instance.age = age; } return instance; } } const instance = singletonMode(); const zs = instance('张三', 18); const ls = instance('李四', 20); console.log(zs === ls); // true 复制代码
class类示例:
class SingletonModeClass { static instanceClass = {}; constructor(name, age) { if (!this.constructor.instanceClass.name) { this.constructor.instanceClass.name = name; this.constructor.instanceClass.age = age; } return this.constructor.instanceClass; } } const zs2 = new SingletonModeClass('张三', 18); const ls2 = new SingletonModeClass('李四', 20); console.log(zs2 === ls2); // true 复制代码
结构型模式
装饰者模式
装饰者模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。即:在不改变原有对象的基础之上,将功能附加到对象上
function函数示例:
function Dog() { this.name = '小黑'; this.age = 2; this.skill = function () { console.log('汪汪汪'); } } const xiaohei = new Dog(); function addWalk(dog) { const skill = dog.skill; dog.skill = function () { skill(); console.log('我可以走'); } }; addWalk(xiaohei); function addRun(dog) { const skill = dog.skill; dog.skill = function () { skill(); console.log('我可以跑'); } }; addRun(xiaohei); xiaohei.skill(); 复制代码
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。
意图:为其他对象提供一种代理以控制对这个对象的访问。
即访问一个对象,不会直接访问到这个对象,而是访问到它的代理对象上,通过代理对象预处理业务逻辑,然后通知被代理对象
优点
1. 职责清晰
2. 高扩展性
3. 可以保护对象
4. 优化性能,减少开销很大的对象,可缓存结果
ES6 proxy 示例:
const person = { name: "张三", age: 18 }; const proxy = new Proxy(person, { get: function (target, propKey, receiver) { console.log(target, propKey, receiver); if (propKey in target) { return target[propKey]; } else { throw new ReferenceError(`没有${propKey}属性`); } }, set: function (target, propKey, value, receiver) { console.log(target, propKey, value, receiver); console.log(`${propKey}属性被设置成了20`); target[propKey] = 20; } }); proxy.name "张三" proxy.sex Uncaught ReferenceError: 没有sex属性 proxy.age = 100 age属性被设置成了20 复制代码
比如明星的很多事务,都是经纪人帮忙处理,比如买东西,是否接受某个代言等
// 定义一个鞋子类 const Shoes = function (name) { this.name = name; }; Shoes.prototype.getName = function () { return this.name; }; // 添加了一个business方法,通过当前的时间来判断是否能买到鞋子 Shoes.prototype.business = function () { const curTime = new Date().getHours(); return curTime >= 8 && curTime <= 20 ? `买到一双${this.getName()}` : '非营业时间!'; } // 定义一个助理对象 const assistant = { buyShoes: function (shoes) { star.buyShoes(shoes.business()); }, pickUpWork: function (work) { if (work.amt < 100000) { star.pickUpWork('出场费小于10万不接') } else if (!work.brand.includes('china')) { star.pickUpWork('不是国产品牌不接') } else { star.pickUpWork(`接了一个${work.brand}的活动,出场费${work.amt}`) } } }; // 定义一个明星对象 const star = { buyShoes: function (res) { console.log(res); }, pickUpWork: function (res) { console.log(res); }}; assistant.buyShoes(new Shoes('鳄鱼皮鞋')); // 买了一双鳄鱼皮鞋||非营业时间! assistant.pickUpWork({ amt: 100, brand: '李宁 china' }); // 出场费小于10万不接 assistant.pickUpWork({ amt: 1000000, brand: 'Nike' }); // 不是国产品牌不接 assistant.pickUpWork({ amt: 1000000, brand: '李宁 china' }); // 接了一个李宁 china的活动,出场费1000000 复制代码
还有比如大图加载前会有一大块空白,这个时候一般要显示loading图或者占位图,也可以用代理模式的思想完成,如下:
const myImage = (function () { // 创建dom const imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function (src) { imgNode.src = src; } } })(); const preImage = (function () { // 创建虚拟图片dom const img = new Image(); // 要加载的图片完成后,替换src img.onload = function () { myImage.setSrc(img.src); }; return { // 先给dom设置loading图片 setSrc: function (src) { myImage.setSrc('https://z3.ax1x.com/2021/03/29/cCjyPf.png'); // 设置虚拟图片dom的src img.src = src; } } })(); preImage.setSrc('https://s1.ax1x.com/2020/05/09/YlFytA.jpg'); 复制代码
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,它结合了两个独立接口的功能,就像常见的mac转接头。示例如下:
// 普通接口 class Mac { getInterface() { return 'Mac接口'; } } // 转换器 class Converter { constructor() { // 转换接口 this.MacInstance = new Mac(); } transfer() { // 获取要转换的接口 const MacInterface = this.MacInstance.getInterface(); return `将${MacInterface}转为通用接口` } } // 实例化转换器 const converter = new Converter(); // 调用转换功能 console.log(converter.transfer()); // 将Mac接口转为通用接口 复制代码
行为型模式
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
class类示例:
// 被观察者,调用setState时触发观察者update class Subject { constructor() { this.count = 0; // 观察者集合 this.observerList = []; } // 获取state getCount() { return this.count; } // 设置state setState(count) { this.count = count; this.notifyAllObservers(); } // 添加观察者 addObserver(observer) { this.observerList.push(observer); } // 通知观察者更新 notifyAllObservers() { this.observerList.forEach(observer => { observer.update(); }) }} // 观察者 被观察者调用setState时触发update class Observer { constructor(name, subject) { this.name = name; // 保存被观察者 this.subject = subject; // 被观察者添加this观察者 this.subject.addObserver(this); } // 更新 update() { console.log(`${this.name}更新,count=${this.subject.getCount()}`) } } let subjectObj = new Subject(); let observer1 = new Observer('observer1', subjectObj); let observer2 = new Observer('observer2', subjectObj); subjectObj.setState(1); subjectObj.setState(2); 复制代码
以上就是个人总结的JS中的设计模式,如果有错误或者不严谨的地方,请给予指正,十分感谢!