定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案
单例模式
定义:在一个软件运行环境中,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现:创建一个函数,返回一个对象实例。函数中先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,在不同的作用域中使用同一个方法进行获取对象实例。
适用场景:
-
vue中的Store;
-
一个单一的Class对象;
-
axios在不使用create的情况下就是一个单利;
实现
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
}
// 方式一
var instance = null;
function init(name) {
var instance = null;
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
const A = init('zhangsan')
// 方式二: 代理
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 测试
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的
console.log(a === b); //true
发布订阅(观察者)模式
观察者模式中通常有两个模型,一个观察者(observer)和一个被观察者(Observed)。从字面意思上理解,即被观察者发生某些行为或者变化时,会通知观察者,观察者根据此行为或者变化做出处理。
场景:
-
vue2.0版本的 数据变化后通知试图更新
-
vue 的eventbus传值方式
-
商品倒计时场景,使用一个定时器来通知所有的商品倒计时
-
许多,对象解耦操作和业务逻辑事件回调操作
export default class EventBus{
constructor() {
this.subs = {}
},
// 存储-创建的订阅者
on(event, cb) {
(this.subs[event] || (this.subs[event] = [])).push(cb)
},
// 通知-观察者
trigger(event, ...args) {
this.subs[event] && this.subs[event].forEach(cb => {
cb(...args)
})
},
// 只订阅一次
once(event, onceCb) {
const cb = (...args) => {
onceCb(...args)
this.off(event, onceCb)
}
this.on(event, cb)
},
// 移除某个观察者
off(event, offCb) {
if (this.subs[event]) {
let index = this.subs[event].findIndex(cb => cb === offCb)
this.subs[event].splice(index, 1)
if (!this.subs[event].length) delete this.subs[event]
}
}
}
使用
// 触发通知
this.$eventEmitter.trigger("onectGetUserinfo",{a:1})
// 接收器
var self = this;
this.$eventEmitter.on("onectGetUserinfo", (e) => {
// do someing
console.log(e)
});
工厂模式
工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
适用场景
- 动态表单核心组件
component:
<component
:is="item.type" // 要渲染的组件类型
:type="item.prefixId" // 组件标识
:cusAttrs="item.componentsProps"
:cusListeners="item.function"
@modelWatch="modelWatch"
/>
- 需要依赖具体环境创建不同实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更大的灵活性
class Product {
constructor(name) {
this.name = name
}
init() {
console.log('init')
}
fun() {
console.log('fun')
}
}
// 工厂 [ˈfæktri]
class Factory {
create(name) {
return new Product(name)
}
}
// use
let factory = new Factory()
let p = factory.create('p1')
p.init()
p.fun()
装饰器模式(Decorator)
定义:在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。许多面向对象的语言都有这项功能,目前有一个提案将其引入了 ECMAScript。
装饰者模式适用的场景:
-
原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;
-
函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。
-
动态地给某个对象添加一些额外的职责,是一种实现继承的替代方案
函数的装饰
function doSomething(name) {
console.log('Hello, ' + name);
}
function loggingDecorator(wrapped) {
return function() {
console.log('Starting');
const result = wrapped.apply(this, arguments);
console.log('Finished');
return result;
}
}
const wrapped = loggingDecorator(doSomething);
用AOP装饰函数实现装饰者模式
Function.prototype.before = function(beforefn) {
var self = this; //保存原函数引用
return function(){ //返回包含了原函数和新函数的 '代理函数'
beforefn.apply(this, arguments); //执行新函数,修正this
return self.apply(this,arguments); //执行原函数
}
}
Function.prototype.after = function(afterfn) {
var self = this;
return function(){
var ret = self.apply(this,arguments);
afterfn.apply(this, arguments);
return ret;
}
}
var func = function() {
console.log('2');
}
//func1和func3为挂载函数
var func1 = function() {
console.log('1');
}
var func3 = function() {
console.log('3');
}
func = func.before(func1).after(func3);
func();
// 1
// 2
// 3
类装饰
@testable
class MyTestableClass {
// ...
}
// 默认方式 (静态属性)
function testable(target) {
target.isTestable = true; //
}
MyTestableClass.isTestable // true
// 实例属性
function testable(target) {
target.prototype.isTestable = true;
}
let obj = new MyTestableClass();
obj.isTestable // true
// 加参
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
适配器模式
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
class Plug {
getName() {
return 'iphone充电头';
}
}
class Target {
constructor() {
this.plug = new Plug();
}
getTypeC() {
return this.plug.getName() + '对接 Type-c充电头 给某米充电';
}
getLightning() {
return this.plug.getName() + '对接 lightning充电头 给某果充电';
}
}
let target = new Target();
if(!🍎){
target.getTypeC(); // iphone充电头 对接 Type-c充电头 给某米充电
} else {
target.getLightning(); // iphone充电头 对接 lightning充电头 给某果充电
}
代理模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
案例一 :假设当 A 在心情好的时候收到花,小明表白成功的几率有60%,而当A 在心情差的时候收到花,小明表白的成功率无限趋近于0。
小明跟A 刚刚认识两天,还无法辨别A 什么时候心情好。如果不合时宜地把花送给A,花被直接扔掉的可能性很大,这束花可是小明吃了7 天泡面换来的。
但是A 的朋友B 却很了解A,所以小明只管把花交给B,B 会监听A 的心情变化,然后选择A 心情好的时候把花转交给A。
let Flower = function() {}
let xiaoming = {
sendFlower: function(target) {
let flower = new Flower()
target.receiveFlower(flower)
}
}
let B = {
receiveFlower: function(flower) {
A.listenGoodMood(function() {
A.receiveFlower(flower)
})
}
}
let A = {
receiveFlower: function(flower) {
console.log('收到花'+ flower)
},
listenGoodMood: function(fn) {
setTimeout(function() {
fn()
}, 1000)
}
}
xiaoming.sendFlower(B)
案例二:图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。
var imgFunc = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image();
img.onload = function() {
imgFunc.setSrc(this.src);
}
return {
setSrc: function(src) {
imgFunc.setSrc('./loading,gif');
img.src = src;
}
}
})();
proxyImage.setSrc('./pic.png');