创建型模式
工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,它提供了一种创建对象的最佳方式。
个人理解类似于工厂车间,可以生产指定产品
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中的设计模式,如果有错误或者不严谨的地方,请给予指正,十分感谢!