event

## 开场介绍 5分钟
* 确认上课的环境 * 自我介绍 * 课程内容 EventEmitter     EventEmitter是Node.js的内置模块events提供的一个类,它是Node事件流的核心,EventEmitter是服务端的东西,     前端已经有event-emitter的npm库     地址: https://www.npmjs.com/package/event-emitter           高级浏览器也有原生提供的EventTarget这种实现事件监听和触发的API     地址: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget           但是它们和Node.js的事件API都有或多或少的区别,今天我们就来实现一个前端版本的EventEmitter     我本章demo的github地址如下     https://github.com/penghuwan/event-emitter
* 引导学员加微信

1. 咱们为什么要学这堂课? * 了解观察者模式和 发布订阅模式 的区别  * 不要一味的使用别人的插件, 自己可以动手造*吗, 自己能开发插件供别人学习吗?
2. 封装完这个 EventEmitter有什么作用? 如何运用到项目中去? * 可以简单演示下在项目中的使用?
## 观察者和发布订阅模式的区别

## 项目中常见的发布订阅模式 
* 父子组件传值 子组件 v-on:eventKK   -   父组件 this.$emit('eventKK') * *数据总线 ```javascript     var bus = new Vue()     // 触发组件 A 中的事件     bus.$emit('id-selected', 1)     // 在组件 B 创建的钩子中监听事件     bus.$on('id-selected', function (id) {     // ...     }) ``` * vuex状态管理
### 二、API介绍 我们要实现的API有:
* on(event, listener):为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。 * emit(event, [arg1], [arg2]): 按监听器的顺序执行执行每个监听器 * addListener(event, listener):on的同名函数(alias) * once(event, listener): 和on类似,但只触发一次,随后便解除事件监听 * removeListener(event, listener): 移除指定事件的某个监听回调 * removeAllListeners([event]):移除指定事件的所有监听回调 * setMaxListeners(n):用于提高监听器的默认限制的数量。(默认10监听回调个产生警告)
![avatar](./pic/event.png)

eventEmitter具体代码
1. 首先我们需要写一个EventEmitter构造函数,给它设置两个属性listeners和maxListener
```javascript // 采用发布订阅模式 class EventEmitter {     constructor(max){         this.listeners = {};           //用于存放事件监听器函数,结构为:         // {         //     "event1": [f1,f2,f3],         //     "event2": [f4,f5],         //     ...         //   }         this.maxListener = max || 10;          //设置的某个事件能够添加的监听器的最大数量,超过这个值,需要在控制台输出警告,但不会报错阻止
    }     //监听事件     // 1.判断该事件的监听器数量是否已超限,超限则报警告     // 2.判断该事件监听器数组是否初始化,若未初始化,则将listeners[event]初始化为数组,并加入监听器cb     // 3.若监听器数组已经被初始化,则判断数组中是否已存在cb,不存在则添加,已存在则不做操作。     on(event,cb){         var listener = this.listeners;         //step 1 判断该事件的监听器数量是否已超限,超限则报警告         if(listener[event] && listener[event].length>=this.maxListener) {             throw console.error('监听器的最大数量是%d,您已超出限制', this.maxListener)         }         //step 2 判断数组是否已初始化         // Array.isArray(listener[event])         // Object.prototype.toString.call(test) == '[object Array]'         if(listener[event] instanceof Array) {             //已经初始化,无需重复添加             if(!listener[event].includes(cb)){                 listener[event].push(cb);             }          } else {             listener[event] = [];             listener[event].push(cb);         }
    }     // 指定addListener等于on方法     addListener(){         this.on.apply(this,arguments);     } //  (1)通过Array.from(arguments)取出方法的参数列表args, // (2)调用args.shift踢掉数组第一个参数即event,留下来的这些是要传给监听器的 // (3)遍历监听器,通过apply方法把上面得到的args参数传进去     emit(event){         //判断有没有这个event         if(!this.listeners[event]){             return;         }         //因为可能会有参数          var args = Array.from(arguments);             args.shift();
         //循环遍历所有监听到event的事件 ,统一执行         // this.listeners[event].forEach((cb,index)=>{         //     cb.apply(null,args);         // })         // 简写为:         this.listeners[event].forEach(cb=>cb.apply(null,args))     }
    // 1.通过indexOf确定监听器回调在数组listeners[event]中的位置     // 2.通过splice(i,1)删除之     removeListener(event,listener){          //判断有没有这个event          var listeners = this.listeners[event];          if(listeners && listeners.length>0){             var i = listeners.indexOf(listener);             if (i >= 0) {                 listeners.splice(i, 1);             }          }     } // 添加事件监听,只能执行一次  // once方法是on方法和removeListener方法的结合:  // 用on方法监听,在回调结束的最后位置,通过removeListener删掉监听函数自身     once(){         var self = this;         function fn() {             var args = Array.prototype.slice.call(arguments);             listener.apply(null, args);             self.removeListener(event, fn);         }         this.on(event, fn)     }      // 清空listeners[event]数组     removeAllListener(){         this.listeners[event] = [];     }     setMaxListeners(num){         this.maxListener = num;     }
} ``` html部分
```javascript <body>     <div id="itapp">         <button type="button" id="btnTest">测试</button>     </div>     <script src="./main.js"></script>     <script>             var EmitterEx = new EventEmitter();         function fn1(data){             console.log(data)             console.log('监听laney');         }         function fn2(){             console.log('监听song');         }         EmitterEx.on('laney',fn1);         EmitterEx.on('song',fn2);
        EmitterEx.emit('laney',{             name:'laney',             age:'20'         })     </script> </body> ```

index.html测试使用 
<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <title>event emiter</title> </head> <body>     <div id="itapp">         <button type="button"  id="btnTest">测试</button>     </div>     <script src="./main.js"></script>     <script>                  var EmitterEx = new EventEmitter();
          // //测试 最多监听的 总数 setMaxListeners,这个需要在监听之前调用         EmitterEx.setMaxListeners(1);
        //测试 on emit         function fn1(data){             console.log(data)             console.log('监听laney');         }         function fn11(data){             console.log(data)             console.log('监听laney1');         }         function fn2(){             console.log('监听song');         }         EmitterEx.addListener('laney',fn1) //监听方式一         // EmitterEx.on('laney',fn1);  //监听方式二         EmitterEx.on('song',fn2);
        // EmitterEx.on('laney',fn11);         // EmitterEx.on('song',fn2);
        btnTest.onclick = function(){             EmitterEx.emit('laney',{                 name:'laney',                 age:'20'             })             EmitterEx.emit('song',{                 name:'song',                 age:'1'             })         }         
           // //测试 removeListener         //    EmitterEx.removeListener('laney',fn1)
        // //测试  removeAllListener         // EmitterEx.removeAllListener();
            </script> </body> </html>
上一篇:springboot mvc 项目启动过程探究2--spring框架的观察者模式


下一篇:2、PVID(本征VLAN)实验配置步骤