首先我们在讲 jQuery 的时候穿插一下 Event Loop
事件轮询(客户端 Event Loop)
JS 代码的同步异步执行机制
调用栈 : 专门用来执行代码的栈
LIFO: last in first out
队列 : 异步任务排队的位置
事件队列: Event queue
微任务队列: Microsoft Queue
Promise.then()
宏任务队列: Macrosoft Queue
整体代码
定时器
FIFO: first in first out
WEB APIs : 提供异步机制的
分配任务到哪一个队列
会在每一次调用栈空的时候进行 计时 和 分配
Event Loop
轮流询问 宏任务队列 和 微任务队列
从 宏任务 开始, 一个宏任务, 清空一次微任务队列
再一个宏任务, 清空一次微任务队列
直到微任务队列清空完毕, 再次访问宏任务队列也没有任务的时候
EventLoop 暂停
设计模式
书写代码的方式
目的是解决特定问题给出的简洁而优化的解决方案
之前接触过设计模式
懒惰模式、沙箱模式
另外的四个设计模式
单例模式
单: 单一, 一个
例: 实例
一个构造函数一生只能有一个实例
不管你 new 多少次, 都是一个实例
应用:
自定义弹出层
结构: div > xxx, 默认隐藏, 在一定条件下显示
实现: 每次显示都是一个新的 div 还是一个 div 来回来去的显示
这里有两个问题需要注意
1. 书写代码的时候和 构造函数已经没有关系
2. new 关键字没有了
function Person() {
this.name = 'Jack'
}
// 加入单例模式核心代码
let instance = null
function singleTon() {
if (!instance) instance = new Person()
return instance
}
//创建并使用 singleTon
//当我第一次调用 singleTon 的时候, instance 是 null
//就要执行 new Person 给 instance 赋值, 从此以后, instance 就是一个 Person 的实例了
//返回 instance
const p1 = singleTon()
//当我第二次调用 singleTon 的时候
//此时 instance 是第一次 new 出来的实例
//if 条件就不会执行了, 直接返回第一次实例的地址
const p2 = singleTon()
console.log(p1, p2)
console.log(p1 === p2)
复制代码
观察者模式
举例: 监控
我们坐在教室里就是 被观察者
监控后面的老师, 就是 观察者
当被观察者触发了一些条件的时候, 观察者就会触发技能
观察者模式: 监控一个 对象 的状态, 一旦状态发生变化, 马上触发技能
需要两个构造函数来实现
1. 创建被观察者
属性, 自己的状态
队列, 记录都有谁观察着自己, 数组[]
方法, 能设置自己的状态, 当我需要改变的时候, 要触发这个方法改变状态
能够添加和删除观察者
2. 创建观察者
需要一个身份证明和一个技能
// 观察者构造函数
class Observer {
constructor (name, fn = () => {}) {
this.name = name
this.fn = fn
}
}
// 创建两个观察者
const njzr = new Observer('年级主任', (state) => { console.log('因为: ' + state + ' , 你是哪个年级的') })
const bzr = new Observer('班主任', (state) => { console.log('因为: ' + state + ' , 明天叫你家长来学校') })
const xz = new Observer('校长', (state) => { console.log('因为: ' + state + ' , 训斥你的班主任') })
// 被观察者的构造函数
class Subject {
constructor (state) {
// 记录自己的状态
this.state = state
// 数组用来保存观察着我的人
this.observsers = []
}
// 设置自己的状态
setState (val) {
this.state = val
// 把 我的所有观察者 的技能都触发
this.observsers.forEach(item => {
// item 就是每一个观察者, 就是一个一个的对象
// 把每一个观察者的技能触发掉
// 告诉他我改变成了什么状态
item.fn(this.state)
})
}
// 添加观察着
addObserver (obs) {
// 谁是观察着, 是就传递谁进来
// 判断一下, 如果观察者已经存在, 就不再添加了
this.observsers = this.observsers.filter(item => item !== obs)
this.observsers.push(obs)
}
// 删除观察者
delObserver (obs) {
// 把 obs 观察者删除,直接使用 filter 方法
this.observsers = this.observsers.filter(item => item !== obs)
}
}
// 创造一个被观察者
const xiaoli = new Subject('学习')
xiaoli.addObserver(njzr)
xiaoli.addObserver(bzr)
xiaoli.addObserver(xz)
const xiaowen = new Subject('读书')
xiaowen.addObserver(bzr)
xiaowen.addObserver(xz)
console.log(xiaoli)
console.log(xiaowen)
复制代码
发布订阅模式
有对象, 就会有人一直看着他
当这个对象发生变化的时候, 第三方通知这个看着的人, 触发技能
举例: 买书
1. 普通程序员买书
去书店, 问, 没有, 回家
过一会再去, 问, 没有, 回家
2. 发布订阅的程序员
去书店, 问, 没有, 留下一个联系方式给店员
一旦有了书, 就会接到电话
触发技能去买书
只需要一个构造函数
创建一个第三方店员的身份
我们的任务就是模拟一个 addEventListener()
// 创建一个第三方观察着构造函数
class Observer {
constructor () {
this.message = {}
}
// 创建一个第三方观察着构造函数
class Observer {
constructor () {
this.message = {}
}
// 直接进行 push
this.message[type].push(fn)
}
// 2. 删除消息队列里面的内容
off (type, fn) {
// 判断 如果 fn 不存在, 只有一个参数的情况
if (!fn) {
// 直接把这个事情取消掉
delete this.message[type]
return
}
// 3. 触发消息队列
trigger (type) {
// 判断是不是有订阅过
if (!this.message[type]) return
// 找到这个数组, 把里面的每一个函数触发
this.message[type].forEach(item => {
// item 就是每一个函数
item()
})
}
}
// 使用构造函数创建一个实例
const person1 = new Observer()
// 当你向拜托这个 person1 帮你观察一些内容的时候
// 告诉你一个行为, 当行为出现的时候, 告诉你干什么
person1.on('a', handlerA)
person1.on('b', handlerB)
// 告诉 person1, 我这个事情不用你关了
// 1. 我只告诉你这个事情不用你管
// person1.off('a') // 把 消息队列 里面属于 a 的数组清空掉
// person1.off('a', handlerA) // 告诉你 a 发生的时候, 不用做 handerA 这个事情了
person1.trigger('a')
// person1 这个人一旦触发 a 行为, 就要把后面的所有事件处理函数执行掉
console.log(person1)
function handlerA() { console.log('handlerA') }
function handlerB() { console.log('handlerB') }
复制代码
策略模式
一个问题匹配多个解决方案, 不一定要用到哪一个
而且有可能随时还会继续增加多个方案
举例: 购物车结算
当我们有好多种折扣计算方式
满 100 减 10
满 200 减 25
普通会员 8 折
高级会员 7 折
1. 把我们的多种方案, 以闭包的形式保存起来
按照传递进来的折扣和价格计算最终价格返回
2. 留下添加折扣和删除折扣的接口
函数也是一个对象
可以把函数名当作一个对象, 向里面添加一些成员
// 2.留下添加折扣和删除折扣的接口
const calcPrice = (function () {
const sale = {
'100_10': function (price) { return price -= 10 },
'200_25': function (price) { return price -= 25 },
'80%': function (price) { return price *= 0.8 }
}
function calcPrice(price, type) {
if (!sale[type]) return '没有这个折扣'
return sale[type](price)
}
// 把函数当作一个对象, 象里面添加一些成员
calcPrice.add = function (type, fn) {
// 专门用来添加折扣
// 判断这个折扣是不是存在
if (sale[type]) return '该折扣已经存在'
// 代码来到这里, 表示折扣不存在
sale[type] = fn
return '添加成功'
}
// 删除一个折扣
calcPrice.del = function (type) {
// 把对应的折扣删除掉
delete sale[type]
}
return calcPrice
})()
// 使用的时候
// 添加一个折扣
calcPrice.add('70%', function (price) { return price *= 0.7 })
const res = calcPrice(320, '70%')
console.dir(res)
calcPrice.del('100_10')
const res2 = calcPrice(320, '100_10')
console.log(res2)
复制代码
最后这里特殊说明一下
观察者 和 发布订阅 两个设计模式
市场上, 有一部分人认为是 一个, 一部分人认为是 两个
我个人认为是两个。
评论