# 观察者模式
# 介绍
- 发布 & 订阅
- 一对多
# 示例
- 点咖啡,点好之后坐等被叫
# UML 类图
# 代码演示
// 主题,保存状态,装填变化之后触发所有观察者对象
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
attach(observer) {
this.observers.push(observer)
}
}
// 观察者
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}
// 测试
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)
s.setState(2)
// o1 update, state: 2
// o2 update, state: 2
// o3 update, state: 2
# 场景
# 网页事件绑定
<button id="btn1">btn</button>
<script>
$('#btn1').click(function() {
console.log(1)
})
$('#btn1').click(function() {
console.log(2)
})
$('#btn1').click(function() {
console.log(3)
})
</script>
# Promise
function loadImg(src) {
let promise = new Promise((resolve, reject) => {
let img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('图片加载失败')
}
img.src = src
})
return promise
}
let src = 'https://img.alicdn.com/tfs/TB1Ly5oS3HqK1RjSZFPXXcwapXa-238-54.png'
let result = loadImg(src)
result.then(function(img) {
console.log(`width:${img.width}`)
return img
}).then(function(img) {
console.log(`height:${img.height}`)
}).catch(function(e) {
console.log(e)
})
# jQuery callbacks
var callbacks = $.Callbacks() //注意大小写
callbacks.add(function (info) {
console.log('fn1', info)
})
callbacks.add(function (info) {
console.log('fn1', info)
})
callbacks.add(function (info) {
console.log('fn1', info)
})
callbacks.fire('gogogo')
callbacks.fire('fire')
# nodejs 自定义事件
# 监听触发事件
const EventEmiter = require('events').EventEmitter
const emitter1 = new EventEmiter()
// 监听 some 事件
emitter1.on('some', info => {
console.log('fn1', info)
})
// 监听 some 事件
emitter1.on('some', info => {
console.log('fn2', info)
})
// 触发 some 事件
emitter1.emit('some', 'xxxx')
// fn1 xxxx
// fn2 xxxx
# 继承监听触发事件
// 继承
const EventEmiter = require('events').EventEmitter
class Dog extends EventEmiter {
constructor(name) {
super()
this.name = name
}
}
const d = new Dog('dog')
d.on('bark', function() {
console.log(this.name, 'bark-1')
})
d.on('bark', function() {
console.log(this.name, 'bark-2')
})
setInterval(function() {
d.emit('bark')
}, 1000)
# 流读取文件
// stream 用到自定义事件
const fs = require('fs')
const readStream = fs.createReadStream('./data/file.txt')
let length = 0
readStream.on('data', function (chunk) {
let len = chunk.toString().length
console.log('len', len)
length += len
})
readStream.on('end', function() {
console.log('length', length)
})
// 监听每一行
const fs = require('fs')
const readline = require('readline')
let rl = readline.createInterface({
input: fs.createReadStream('./data/file.txt')
})
let lineNum = 0
rl.on('line', function(line) {
// line 就是每一行的数据
lineNum++
})
rl.on('close', function() {
console.log('linenum', lineNum)
})
# 其他场景
- nodeJS 中:处理 http 请求;多进程通讯
// http 处理请求
function serverCallback(req, res) {
var method = req.method.toLowerCase() // 获取请求的方法
if(method === 'get') {
// 处理get请求的代码
}
if(method === 'post') {
// 接收 post 请求的内容
var data = ''
req.on('data', function(chunk) {
// 一点一点 接收内容
data += chunk.toString()
})
req.on('end', function() {
// 接收完毕,将内容输出
res.writeHead(200, {'Content-type': 'txt/html'})
res.write(data)
res.end()
})
}
}
// 多进程通讯
var cp = require('child_process')
var n = cp.fork('./sub.js')
n.on('message', function(m) {
console.log('PARENT got message:' + m)
})
n.send({hello: 'work'})
// sub.js
process.on('message', function(m) {
console.log('CHILD go message:' + m)
})
process.send({foo: 'bar'})
- vue 和 React 组件生命周期触发
// react 组件
class Login extends React.Component {
constructor(props, context) {
super(props, context)
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this)
this.state = {
checking: true
}
}
render() {
return(
<div>
<header title="登录" history = {this.props.history}></header>
</div>
)
}
componentDidMount() {
// 判断是否已经登录
this.doCheck()
}
}
- vue watch
var vm = new Vue({
el: '#demo',
data: {
fileName: 'Foo',
laseName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName(val) {
this.fullName = val + ' ' + this.laseName
},
lastName(val) {
this.fullName = this.firstName + '' + val
}
}
})
# 设计原则验证
- 主题和观察者分离,不是主动触发而是被动监听,两者解耦
- 符合开放封闭原则
阅读量: