1、单例模式
1. 定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点
2. 核心
确保只有一个实例,并提供全局访问
第一种:类
// 单例
let __instance = (() => {
let instance;
return (newInstance) => {
if (newInstance) instance = newInstance;
return instance;
}
})();
class bleSdk {
constructor () {
if (__instance()) return __instance();
this.foo = 'bar';
__instance(this);
}
}
export {
bleSdk
};
js页面
import {bleSdk} from "../bleSdk"
Page({
data: {
},
onLoad() {
let ul = new bleSdk();
let ul2 = new bleSdk();
console.log("判断是否为单例",ul === ul2)//true
},
})
第二种:在某个方法里面使用
function SetManager(name) {
this.manager = name;
console.log("初始化类", name)
}
SetManager.prototype.getName = function () {
console.log(this.manager);
};
var SingletonSetManager = (function () {
var manager = null; //使用闭包缓存一个内部变量
return function (name) {
if (!manager) {
manager = new SetManager(name);
}
return manager;
}
})();
SingletonSetManager('a').getName(); // a
SingletonSetManager('b').getName(); // a
SingletonSetManager('c').getName(); // a
2、状态模式
例子:比如超级玛丽,就可能同时有好几个状态比如 跳跃,移动,射击,蹲下 等,如果对这些动作一个个进行处理判断,需要多个if-else或者switch不仅丑陋不说,而且在遇到有组合动作的时候,实现就会变的更为复杂,这里可以使用状态模式来实现。
const SuperMarry = (function () {
let _currentState = [], // 状态数组
states = {
jump() {
console.log('跳跃!')
},
move() {
console.log('移动!')
},
shoot() {
console.log('射击!')
},
squat() {
console.log('蹲下!')
}
}
const Action = {
changeState(arr) { // 更改当前动作
_currentState = arr
return this
},
goes() {
console.log('触发动作')
_currentState.forEach(T => states[T] && states[T]())
return this
}
}
return {
change: Action.changeState,
go: Action.goes
}
})()
// 调用
SuperMarry.change(['jump', 'shoot'])
.go() // 触发动作 跳跃! 射击!
.go() // 触发动作 跳跃! 射击!
.change(['squat'])
.go() // 触发动作 蹲下!
使用es6优化:
class SuperMarry {
constructor() {
this._currentState = []
this.states = {
jump() {console.log('跳跃!')},
move() {console.log('移动!')},
shoot() {console.log('射击!')},
squat() {console.log('蹲下!')}
}
}
change(arr) { // 更改当前动作
this._currentState = arr
return this
}
go() {
console.log('触发动作')
this._currentState.forEach(T => this.states[T] && this.states[T]())
return this
}
}
//使用
new SuperMarry()
.change(['jump', 'shoot'])
.go() // 触发动作 跳跃! 射击!
.go() // 触发动作 跳跃! 射击!
.change(['squat'])
.go() // 触发动作 蹲下!
状态模式的使用场景也特别明确,有如下两点:
- 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
- 一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示。
简而言之,当遇到很多同级if-else或者switch的时候,可以使用状态模式来进行简化。
3、观察者模式
class Subject {
constructor() {
this.subs = [];
}
addSub(sub) { //订阅
this.subs.push(sub);
console.log("XX订阅了:",this.subs)
}
removeSub(sub) { //移除订阅
const idx = this.subs.findIndex(i => i === sub);
if (idx === -1) {
console.log('不存在该观察者');
return;
}
this.subs.splice(idx, 1);
}
notify() { //通知
this.subs.forEach(sub => {
sub.update(); // 与观察者原型方法update对应!
});
}
}
// 观察人,相当于订阅者
class Observer {
update() {
console.log('被动触发:update');
}
}
export {
Subject,
Observer
};
index.js使用:
import {
Subject,
Observer
} from "../subObserver"
Page({
data: {
},
onLoad: function () {
const subject = new Subject();
const ob = new Observer(); //订阅者一
const ob2 = new Observer(); //订阅者二
ob2.update = function () { //修改update方法,实现不同逻辑
console.log('被动触发:我是修改后的方法');
}
subject.addSub(ob);
subject.addSub(ob2);
//目标发布消息调用观察者的更新方法了。需要主动调用notify来触发
subject.notify();
// 被动触发:update
// 被动触发:我是修改后的方法
subject.removeSub(ob2); //移除之后就不触发ob2的update了,不再打印`laifeipeng`
subject.notify();
//被动触发:update
// 不存在该观察者
},
})
4、代理模式
应用场景:图片的预加载,蓝牙各种回调处理
图片的预加载: 平时由于网络的不佳,导致图片出来前会有一片空白。所以我们限用一张 loading 图片占位,在异步方式加载图片
onLoad: function () {
const that = this
var myImage = function () {
return {
setSrc: function (src) {
that.setData({
urlSrc: src
})
console.log("标志0", src)
}
}
};
// 代理:加载一个默认的图片占位,等服务器返回了图片url再更新
var proxyImage = function () {
const onload = function () {
setTimeout(() => { //模仿请求服务器
myImage().setSrc("服务器返回了图片的url"); // this指向img!img加载完成后,将img.src传递给myImage
console.log("标志1")
}, 2000)
}
return {
setSrc: function (src) {
myImage().setSrc(src); // loading
onload()
}
}
};
proxyImage().setSrc("./images/loading.gif")
},
5、策略模式
应用场景:表单验证
Validator.js
const strategies = {
isNonEmpty: function (value, errorMsg) {
if (value === '') {
return errorMsg;
}
},
minLength: function (value, length, errorMsg) {
console.log("最小长度==>", value, length, errorMsg)
if (value.length < length) {
return errorMsg;
}
},
maxLength: function (value, length, errorMsg) {
if (value.length > length) {
return errorMsg;
}
},
isMobile: function (value, errorMsg) {
if (!/(^1[3|4|5|6|7|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
class Validator {
constructor() {
this.cache = [];
this.strategies = strategies;
}
add(value, rule, errorMsg) {
const dom = {
value: value
}
const ary = rule.split(':');
this.cache.push(function () {//把匿名方法放到数组里,闭包的示例
var strategy = ary.shift(); //shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
ary.unshift(dom.value); //unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
ary.push(errorMsg);
return strategies[strategy].apply(dom, ary); //调用strategies对象的指定方法对象,并规定在函数对象内部this指向dom元素,ary作为参数传入
});
}
start() {
let msg
this.cache.find(item => {//循环运行匿名函数
msg = item();
if (msg) return msg
})
this.cache = []//清除闭包缓存
return msg;
};
}
module.exports = {
Validator: new Validator()
}
index使用:
import {
Validator
} from "../Validator"
Page({
data: {
},
onLoad: function () {
Validator.add("123456", 'minLength: 10', '用户名长度不能小于10位')
Validator.add("1234567891011", 'maxLength: 10', '用户名长度不能大于10位')
Validator.add("16603003201", 'isMobile', '用户手机不正确')
const result = Validator.start() //Validator.start()运行返回 undefined表示验证的内容都正确3
console.log(result)
if (result) {
return
}
console.log("你好,您的资料全部正确")
},
})
本文介绍了JavaScript中单例模式的两种实现方法,以及状态模式、观察者模式、代理模式和策略模式的应用示例,帮助理解如何在实际项目中运用这些设计模式来优化代码结构和管理对象行为。
215

被折叠的 条评论
为什么被折叠?



