1. 观察者模式
设计模式思路:
- 变化的数据作为被观察者, 受改数据影响的元素/对象作为被观察者
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>观察者模式</title>
<style>
.container {
width: 400px;
background-color: mediumpurple;
margin: 0 auto;
overflow: hidden;
text-align: center;
padding: 30px
}
h2 {
margin: 20px 0;
color: indianred;
}
input {
width: 80%;
border-radius: 5px
}
.boxes {
margin: 30px auto;
width: 80%;
}
.box {
width: 100%;
height: 40px;
background-color: #e5e6e4;
border-radius: 10px;
margin-bottom: 7px;
line-height: 40px;
font-weight: bold;
}
.active {
background-color: #d2561c;
color: #e5e6e4;
}
</style>
</head>
<body>
<div class="container">
<h2>观察者模式实现</h2>
<label>
<input type="text">
</label>
<div class="boxes">
</div>
</div>
<script type="text/javascript">
class Input {
constructor(el) {
this.observers = [];
el.addEventListener("input", e => {
this.notifyAll(e.target.value);
})
}
notifyAll(value) {
this.observers.forEach(ob => {
ob.notify(value)
})
}
addObserver(observer) {
this.observers.push(observer)
}
}
class Observer extends DocumentFragment {
constructor(txt) {
super();
this.div = document.createElement("div")
this.div.className = "box"
this.div.innerText = txt;
document.querySelector(".boxes").appendChild(this.div)
}
notify(value) {
this.div.classList.toggle('active', this.handle(value))
}
}
class DefaultObserver extends Observer {
constructor(txt) {
super(txt);
this.txt = txt;
}
handle(value) {
return value.indexOf(this.txt) !== -1
}
}
class NumberObserver extends Observer {
constructor(txt) {
super(txt);
}
handle(value) {
return /\d/.test(value)
}
}
const ipt = new Input(document.querySelector("input"));
ipt.addObserver(new DefaultObserver("a"));
ipt.addObserver(new DefaultObserver("b"));
ipt.addObserver(new DefaultObserver("c"));
ipt.addObserver(new NumberObserver('包含数字'))
</script>
</body>
</html>
2. 单例模式
2.1 设计原则
- 无论程序员手动创建多少个对象, 存在的示例永远都只有一个
2.2 实现思路
- 使用闭包的特性, 一个函数内部返回另一个函数;
- 如果示例没有被创建过, 那么调用用户传递进来的回调函数,进行对象的创建并返回; 且将结果赋值给返回函数外部的那个变量(result)
- 如果示例已经被创建, 那么result必然不为null, 直接将result返回就可以啦, 这样就不会在去创建一个新对象了
2.3 Code
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>闭包实现单例模式</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
</head>
<body>
<button id="btn">登录</button>
<script>
console.log(window)
const createLogin = function (a, b, c) {
console.log(a, b, c)
const div = document.createElement('div')
div.innerHTML = "登录弹窗"
div.style.display = "none"
document.body.appendChild(div)
return div
}
const getSingle = function (cb) {
let result;
return function () {
return result || (result = cb.apply(this, arguments))
}
}
const create = getSingle(createLogin)
document.getElementById("btn").onclick = () => {
const element = create(1, 2, 3)
element.style = {
display: "block",
color: "red"
}
}
</script>
</body>
</html>
3. 适配器模式
3.1 目的
- 为了解决两个接口不兼容的情况
- 不需要改变已有的接口, 通过包装一层的方式实现两个接口之间的正常协作
const googleMap = {
show() {
console.log("rendering google map...");
}
}
const baiduMap = {
display() {
console.log("rendering baiduMap...")
}
}
const renderMap = function (map) {
if (map.show instanceof Function) {
map.show()
}
}
const baiduMapAdapter = {
show() {
return baiduMap.display()
}
}
renderMap(googleMap)
renderMap(baiduMapAdapter)
4. 装饰模式
4.1 目的
- 不需要改变已有接口, 给对象添加额外的功能
4.2 实现
function readonly(target, key, descriptor) {
descriptor.writeable = false;
return descriptor;
}
@readonly
class Man {
name = "naruto"
}
let m = new Man()
m.name = "sasuke"
console.log(m.name)
5. 代理模式
5.1 目的
- 某些情况下,一个对象不适合或不能直接引用另一个对象, 而代理对象可以再两个对象之间起到中介的作用
5.2 实现
class Car {
drive(): string {
return "driving"
}
}
class Driver {
public age: number;
constructor(age: number) {
this.age = age;
}
}
class CarProxy {
private driver: Driver;
constructor(driver: Driver) {
this.driver = driver;
}
drive(): string {
if (this.driver.age < 18) {
return "to yong to drive"
}
return new Car().drive();
}
}
const driver = new Driver(19);
const result = new CarProxy(driver).drive();
console.log(result);
const obj = {}
const proxy = new Proxy(obj, {
get(target: {}, p: string | symbol, receiver: any): any {
return Reflect.get(target, p, receiver)
},
set(target: {}, p: string | symbol, value: any, receiver: any): boolean {
return Reflect.set(target, p, value, receiver)
}
})
proxy.count = 1;
++proxy.count
console.log(obj.count)
6. 发布, 订阅模式
6.1 目的
- 实现对象间多对对的依赖关系
- 通过事件中心管理多个事件
- 目标对象并不直接通知观察者, 而是通过事件中心来派发通知
6.2 实现
declare interface IPubsub {
list: Array<unknown>,
addListener: (key: string, fn: Function) => void,
publish: () => void
removeListener: (key: string, fn: Function) => void | false
}
const shopObj: IPubsub = {
list: [],
addListener: function (key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
},
publish: function () {
let key = Array.prototype.shift.call(arguments);
const funcs = this.list[key];
if (!funcs || funcs.length === 0) {
console.log("no relevant subscriber")
return
}
for (let i = 0; i < funcs.length; i++) {
funcs[i].apply(this, arguments)
}
},
removeListener(key: string, fn?: Function | null): void | false {
const fns = this.list[key]
if (fn === null) {
fns.length = 0;
return
}
if (!fns) {
fns && (fns.length = 0);
return false
} else {
for (let i = fns.length - 1; i >= 0; i--) {
let _fn = fns[i]
if (_fn === fn) {
fns.splice(i, 1)
}
}
}
}
}
shopObj.addListener("xm", size => {
console.log(`xm size is ${size}`)
})
shopObj.addListener("张三", size => {
console.log(`zhangsan size is ${size}`)
})
shopObj.publish("张三", 120)
shopObj.removeListener("张三", null)
shopObj.publish("张三", 120)
|