事件发布/订阅——理解为:子给父传值 & 1注册事件 $on—— 该父组件是订阅者 & 2触发事件 $emit——该子组件是发布者 & 3测试 可同页面也可不同组件 & .call改变事件处理函数的this指向为事件对象,不然为window
- 观察者模式
- 发布/订阅模式
// 监听一个自定义事件
bus.$on('事件类型', 处理函数)
// 发布事件
bus.$emit('事件类型', 参数)
代码展示:
1.注册事件时,传递参数 剩余参数 …args
2.改变handler事件函数内部的this指向 .call
<body>
<script>
// 发布订阅模式——子给父传值
// 注册事件-父组件接收(订阅消息,消息就是事件)——数据变化了,就更新视图
// bus.$on('click', fn)
// bus.$on('click', fn1)
// 发布事件-子组件传值(发布消息,消息就是事件)
// bus.$emit('click', 事件参数)
//构造函数
function EventEmitter() {
//对象结构: { 'click': [fn, fn1], 'm': [fn] }
// 定义数组或对象,用来存储注册的事件 bus.$on('click', fn)
this.subs = {}
}
// 一、注册事件 $on—— 该父组件是订阅者-订阅消息,随时监听到子组件传了啥,传给我什么,我就变成什么 ——数据变化了,就更新视图
// 1. eventType-事件类型 2. handler-事件处理函数
EventEmitter.prototype.$on = function(eventType, handler) {
// 第一种情况:注册事件 { 'click': [fn] }
// if (this.subs[eventType]) {
// // 如果有值 -->值为: [fn] ,this.subs[eventType] 对应数组 [fn]
//subs 是对象 , [eventType] 变量作为属性,不能用 . ,要用中括号 ;push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
// this.subs[eventType].push(handler)
// } else {
// // 第二种情况: {}
// // 没有值 ,把值添加进去
// this.subs[eventType] = []
// this.subs[eventType].push(handler)
// }
// 上面两种情况,等价如下 释义:如果有值,等于自己;如果无值,等于空数组
this.subs[eventType] = this.subs[eventType] || []
// 数组OK,直接调用
this.subs[eventType].push(handler)
}
// 二、触发事件 $emit——该子组件是发布者-发布消息,用$emit把数据传给父组件
// 在function (eventType, ...args) 此时的args是剩余参数
//...args 指剩余参数, 剩余参数在参数列表的最后,args是数组
EventEmitter.prototype.$emit = function(eventType, ...args) {
if (this.subs[eventType]) {
//事件函数 [fn, fn1] , this.subs[eventType] 存储了事件函数
this.subs[eventType].forEach(handler => {
// 此时的...args 是展开运算符 -显示函数中的参数 a, b, c
// 此时的this就是em对象
// handle函数 指的就是$on注册的function()事件函数 ,
//call-用来改变this 不加 .call ,this指-window全局变量——handler(...args)
handler.call(this, ...args)
//逻辑:箭头函数handler没有自己的this——>上找:.$emit存入的函数function——>.$emit在触发事件时调用 em.$emit ——>$emit()方法里面的this是em对象
})
}
}
// 三、测试
var em = new EventEmitter()
// 1.先注册事件
em.$on('click', function(a, b, c) { // 接收也要是三个参数
console.log(this) //此时的this就是em对象 若不加 .call ,this指-window全局变量——handler(...args)
console.log(a + b + c) // II 显示为 10
console.log('hello') // II 显示为 hello
})
em.$on('click', function() {
console.log('hello1') // II 显示为 hello1
})
em.$on('test', function() {
console.log('test') // II 不显示
})
// 2.再触发事件 传递3个参数
em.$emit('click', 1, 3, 6) // I 触发那哪个事件,就执行哪个;此时,test事件不执行,不显示
</script>
</body>
控制台打印显示为:
示例:
function EventEmitter () {
// 存储所有订阅的消息处理函数
this.subs = {
// 事件类型: [处理函数, 处理函数...]
// a: [],
}
}
EventEmitter.prototype.$on = function (eventType, callback) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(callback)
}
// 参数中的 ... 表示函数的剩余(rest)参数
// 它会把所有参数放到一个数组中
EventEmitter.prototype.$emit = function (eventType, ...args) {
const subs = this.subs[eventType]
if (subs) {
subs.forEach(callback => {
callback(...args)
})
}
}