├── .gitignore ├── .npmignore ├── test ├── lib │ ├── on.listener.js │ ├── maxNum.listener.js │ ├── emit.listenner.js │ ├── remove.listener.js │ ├── once.test.js │ └── remove.all.js └── index.js ├── package.json ├── dist └── events.min.js ├── README.md └── events.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /test/lib/on.listener.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var events = require('../../events.js'); 3 | 4 | var expect = chai.expect; 5 | var addListenerNum; 6 | module.exports = function () { 7 | describe('chick "on"', function () { 8 | addListenerNum = 0; 9 | it('add Listener', function () { 10 | events.on('a', eatting) 11 | 12 | function eatting () { 13 | addListenerNum++; 14 | } 15 | 16 | events.emit('a'); 17 | 18 | expect(addListenerNum).to.be.equal(1); 19 | }) 20 | }) 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "events-manage", 3 | "version": "1.0.2", 4 | "description": "An event management library", 5 | "main": "events.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuchengying/js-Events.git" 12 | }, 13 | "keywords": [ 14 | "event" 15 | ], 16 | "author": "liuchengying", 17 | "license": "MIT", 18 | "url": "https://github.com/liuchengying/js-Events", 19 | "dependencies": { 20 | "chai": "^4.1.2", 21 | "mocha": "^5.2.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/lib/maxNum.listener.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var events = require('../../events.js'); 3 | 4 | var expect = chai.expect; 5 | 6 | module.exports = function () { 7 | describe('chick defaultMaxEventListNum', function () { 8 | it('defaultMaxEventListNum value', function () { 9 | expect(events.defaultMaxEventListNum).to.be.equal(10); 10 | }); 11 | }); 12 | 13 | describe('chick MaxEventListNum', function () { 14 | it('MaxEventListNum value', function () { 15 | events.MaxEventListNum = 15; 16 | expect(events.MaxEventListNum).to.be.equal(15); 17 | }) 18 | }) 19 | } -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var testFnArray = []; 2 | // 将所有模块push到testFnArray中 3 | var addFnToArr = function (path) { 4 | if (!path) { 5 | return; 6 | } 7 | testFnArray.push(require(path)); 8 | } 9 | 10 | addFnToArr('./lib/once.test.js'); 11 | addFnToArr('./lib/maxNum.listener.js'); 12 | addFnToArr('./lib/on.listener.js'); 13 | addFnToArr('./lib/emit.listenner.js'); 14 | addFnToArr('./lib/remove.listener.js'); 15 | addFnToArr('./lib/remove.all.js'); 16 | 17 | // 遍历调用 18 | for(var i = 0 ; i Error: "content" must be a function')}k=this.event_list;if(!k){k=this.event_list={}}else{j=this.event_list[l]}if(!j){j=this.event_list[l]=m;j.ListenerCount=1}else{if(h(j)){j=this.event_list[l]=[j,m];j.ListenerCount=j.length}else{if(c(j)){j.push(m);j.ListenerCount=j.length}}}if(!j.maxed){if(c(j)){var i=j.length;if(i>(this.MaxEventListNum?this.MaxEventListNum:this.defaultMaxEventListNum)){j.maxed=true;console.warn("events.MaxEventListNum || [ MaxEventListNum ] :The number of subscriptions exceeds the maximum, and if you do not set it, the default value is 10")}else{j.maxed=false}}}};e.prototype.emit=function(l,o){var k,j,m=Array.prototype.slice.call(arguments,1);k=this.event_list;if(k){j=this.event_list[l]}else{console.warn('events.prototype.emit || [eventName, content] -> Error: "can not find eventName"')}if(!j){return false}else{if(h(j)){j.apply(this,m)}else{if(c(j)){for(var n=0;n Error: "content" must be a function')}this.on(j,b(this,j,i));return this};e.prototype.removeListener=function(n,o){var k,j,l=0;if(!h(o)){throw new Error('events.prototype.removeListener || [eventName, content] -> Error: "content" must be a function')}k=this.event_list;if(!k){return this}else{j=this.event_list[n]}if(!j){return this}if(h(j)){if(j===o){delete k[n]}}else{if(c(j)){for(var m=0;m npm install --save events-manage 17 | 18 | CMD、AMD模块化下 19 | ``` javascript 20 | // CMD 21 | var events = require('events-manage'); 22 | 23 | // AMD 24 | define('events-manage', function (e) { ... }) 25 | ``` 26 | 浏览器模式下 27 | ``` html 28 | 29 | // 个人的腾讯云 30 | ``` 31 | **提示:** 32 | 重要的事情说的第一遍:在CMD以及node环境下本模块直接返回对象实例(无需再new一个实例),而在AMD和浏览器环境下,则返回构造函数(需要通过new来创建实例) 33 | [js文件戳这里](https://github.com/liuchengying/js-Events/tree/master/dist) 34 | ### 使用 35 | 简单使用 36 | ``` javascript 37 | // 创建events实例 38 | var myEvent = new events(); 39 | 40 | // 重要的事情说的第二遍: 41 | // 在CMD以及node环境下本模块直接返回对象实例(无需再new一个实例),而在AMD和浏览器环境下,则返回构造函数(需要通过new来创建实例) 42 | ``` 43 | 在Vue中使用 44 | ``` javascript 45 | var myEvents = require('events-manage'); 46 | 47 | // 添加全局,会挂在在Vue.$ev下 48 | Vue.use(myEvents); 49 | 50 | // 全局监听 51 | Vue.$ev.on('eatting', function (msg) { 52 | console.log(msg); 53 | }) 54 | 55 | // 发布 56 | Vue.$ev.emit('eatting', '吃饭'); // 吃饭 57 | ... 58 | ... 59 | ... 60 | // 重要的事情说的第三遍: 61 | // 在CMD以及node环境下本模块直接返回对象实例(无需再new一个实例),而在AMD和浏览器环境下,则返回构造函数(需要通过new来创建实例) 62 | ``` 63 | * #### events.MaxEventListNum --属性 64 | > *每个事件的最大订阅数量* 65 | ``` javascript 66 | myEvent.MaxEventListNum = 5;// 每个事件的最多订阅数为 5 67 | ``` 68 | **如果不设置MaxEventListNum, 则默认为 10** 69 | * #### events.getListenerCount([,type]) -- 方法 70 | > *获得当前所有事件/某类型的事件的监听数量* 71 | ``` javascript 72 | myEvents.getListenerCount('eatting') // type 参数为可选参数 73 | 74 | // 简单示例 75 | 76 | // 添加监听事件 77 | myEvents.on('eatting', function () { ... }); 78 | 79 | myEvents.on('sleeping', function () { ... }); 80 | 81 | myEvents.on('eatting', function () { ... }); 82 | 83 | // 获取事件数量 84 | myEvents.getListenerCount('eatting'); // return: 2 85 | 86 | myEvents.getListenerCount('sleeping'); // return: 1 87 | 88 | myEvents.getListenerCount(); 89 | // return: 90 | // { 91 | // eatting: 2, 92 | // sleeping: 1 93 | //} 94 | ``` 95 | 通过上面的简单示例,当使用getListenerCount方法获取监听事件数量时如果存在参数type(事件类型),则该方法会返回 此类型监听事件的数量(返回Number类型),当不存在参数type时,则返回当前myEvents实例所有监听事件,以对象的方式返回(键值对)。 96 | * #### events.emit(enentName, message) --方法 97 | > *发布事件* 98 | ``` javascript 99 | myEvent.emit('发布事件名', '发布信息'); // '发布信息' 给监听这的回调传参 100 | 101 | // 简单示例 102 | 103 | myEvent.emit('sleeping', '睡觉'); 104 | ``` 105 | * #### events.on(eventName, callback) --方法 106 | > *订阅 (监听) 事件* 107 | ``` javascript 108 | myEvent.on('订阅事件的名字', callback); 109 | 110 | // 简单示例 111 | 112 | myEvent.on('sleeping', function (msg) { 113 | console.log('我正在' + msg + '...'); // 我正在睡觉... 114 | }) 115 | ``` 116 | * #### events.once(eventName, callback) --方法 117 | > *只订阅一次事件* 118 | > 即当使用此方法订阅的事件,在接收到发布的事件执行后,立即删除当前订阅 119 | 120 | 面对各种各样的需求,比如,我们希望只监听一次事件,也就是说我们只需要第一次执行,而之后就不在监听此事件。 121 | ``` javascript 122 | // 发布事件 第一次发布 123 | myEvent.emit('eatting', '第一次-吃饭'); 124 | 125 | // 发布事件 的二次发布 126 | myEvent.emit('eatting', '第二次-吃饭'); 127 | 128 | // 使用 once 方法订阅事件 129 | myEvent.once('eatting', function (msg) { 130 | console.log(msg); // 第一次-吃饭 131 | }) 132 | 133 | ``` 134 | 当使用 once 方法订阅事件时,只对事件订阅一次,当第一次 emit 后,once 订阅的事件会立即被删除。所以以上代码实例中,第二次被 emit 后并没有监听到任何eatting事件,无任何输出。 135 | * #### events.removeListener(eventName, callback) --方法 136 | > *删除指定事件的指定订阅(监听)方法* 137 | ``` javascript 138 | // 定义callback 139 | function cb (msg) { 140 | console.log('我正在' + msg + '...') 141 | } 142 | 143 | // 订阅(监听)eatting事件 144 | myEvent.on('eatting', cb); 145 | 146 | // 发布 eatting 事件 147 | myEvent.emit('eatting', '吃饭'); // 我正在吃饭 148 | 149 | // 删除 eatting事件中 回调为 cb 的监听 150 | myEvent.removeListener('eatting', cb); 151 | 152 | // 153 | // 此次发布事件,去没有任何输出 154 | myEvent.emit('eatting', '再一次,吃饭'); /*无任何输出,说明该监听已经被删除*/ 155 | ``` 156 | 当需要对都一个订阅(监听)删除时,参数中callback必须与订阅此事件的方法中的 callback 一致,否则无法删除。 157 | ``` javascript 158 | // 订阅(监听)eatting事件 159 | myEvent.on('eatting', function (msg) { 160 | ... // 相同内容 161 | }); 162 | 163 | // 删除订阅事件 164 | myEvent.removeListener('eatting', function (msg) { 165 | ... // 相同内容 166 | }) 167 | ``` 168 | 此方式无法删除对应的订阅(监听事件),传入的 callback 为匿名函数,在 javascript 中,相同的书写形式的匿名函数是不相等的。所以导致无法删除。 169 | **提示:** 为防止两个callback不一致导致无法删除,建议在订阅事件希望删除的事件时,最好不采用匿名函数的形式。 170 | * #### events.removeAllListener([,eventName]) --方法 171 | > *删除同一事件的所有订阅(监听),或者删除当前events对象的所有订阅(监听)* 172 | 173 | 当传参数eventName时,只删除当前eventName的所有订阅(监听),不影响其他订阅(监听)事件 174 | ``` javascript 175 | // 订阅 176 | myEvent.on('eatting', function (msg) { 177 | console.log(msg); 178 | }); 179 | myEvent.on('eatting', function (msg) { 180 | console.log(msg); 181 | }); 182 | myEvent.on('sleeping', function (msg) { 183 | console.log(msg); 184 | }); 185 | // 发布 186 | myEvent.emit('eatting', '吃饭'); // 吃饭 187 | myEvent.emit('eatting', '第一次,吃饭'); // 第一次,吃饭 188 | myevent.emit('sleeping', '睡觉'); // 睡觉 189 | // 删除 eatting 监听 190 | myEvent.removeAllListener(type); 191 | myEvent.emit('eatting', '吃饭'); // warning 不存在此监听事件 192 | myEvent.emit('sleeping', '睡觉'); // 睡觉 193 | 194 | ``` 195 | 当没有传参数eventName时,删除当前events实例的所有订阅(监听) 196 | ``` javascript 197 | // 订阅 198 | myEvent.on('eatting', function (msg) { 199 | console.log(msg); 200 | }); 201 | myEvent.on('sleeping', function (msg) { 202 | console.log(msg); 203 | }); 204 | // 发布 205 | myEvent.emit('eatting', '吃饭'); // 吃饭 206 | myevent.emit('sleeping', '睡觉'); // 睡觉 207 | // 删除所有 208 | myEvent.removeAllListener(); 209 | // 再次发布 210 | myEvent.emit('eatting', '吃饭'); // warning 不存在此监听事件 211 | myevent.emit('sleeping', '睡觉'); // warning 不存在此监听事件 212 | ``` 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | ------------------------- 222 | 如果感觉对你有帮助,记着STAR:star:!!!:grin: -------------------------------------------------------------------------------- /events.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | ; 3 | (function (global) { 4 | function factory() { 5 | var Events = events; 6 | return Events; 7 | } 8 | // 构造函数 9 | function events() { 10 | events.init.call(this); 11 | } 12 | /** 13 | *初始化 14 | */ 15 | events.init = function () { 16 | if (!this.event_list) { 17 | this.event_list = {}; 18 | } 19 | this.MaxEventListNum = this.MaxEventListNum || undefined; 20 | this.defaultMaxEventListNum = 10; 21 | } 22 | 23 | events.prototype.install = function (Vue, Option) { 24 | Vue.prototype.$ev = this; 25 | } 26 | /** 27 | *订阅、监听器 28 | * 29 | * @param {*} eventName 事件名称 30 | * @param {*} content 回调 31 | */ 32 | events.prototype.on = function (eventName, content) { 33 | var _event, ctx; 34 | if (!isFunction(content)) { 35 | throw new Error('events.prototype.on || [eventName, content] -> Error: "content" must be a function'); 36 | }; 37 | _event = this.event_list; 38 | if (!_event) { 39 | _event = this.event_list = {}; 40 | } else { 41 | ctx = this.event_list[eventName]; 42 | } 43 | if (!ctx) { 44 | ctx = this.event_list[eventName] = content; 45 | ctx.ListenerCount = 1; 46 | } else if (isFunction(ctx)) { 47 | ctx = this.event_list[eventName] = [ctx, content]; 48 | ctx.ListenerCount = ctx.length; 49 | } else if (isArray(ctx)) { 50 | ctx.push(content); 51 | ctx.ListenerCount = ctx.length; 52 | } 53 | 54 | if (!ctx.maxed) { 55 | if (isArray(ctx)) { 56 | var len = ctx.length; 57 | if (len > (this.MaxEventListNum ? this.MaxEventListNum : this.defaultMaxEventListNum)) { 58 | ctx.maxed = true; 59 | console.warn('events.MaxEventListNum || [ MaxEventListNum ] :The number of subscriptions exceeds the maximum, and if you do not set it, the default value is 10'); 60 | } else { 61 | ctx.maxed = false; 62 | } 63 | } 64 | } 65 | 66 | } 67 | 68 | /** 69 | *发布、执行器 70 | * 71 | * @param {*} eventName 事件名称 72 | * @param {*} content 回调 73 | */ 74 | events.prototype.emit = function (eventName, content) { 75 | var _event, ctx, args = Array.prototype.slice.call(arguments, 1); 76 | _event = this.event_list; 77 | if (_event) { 78 | ctx = this.event_list[eventName]; 79 | } else { 80 | console.warn('events.prototype.emit || [eventName, content] -> Error: "can not find eventName"'); 81 | } 82 | if (!ctx) { 83 | return false; 84 | } else if (isFunction(ctx)) { 85 | ctx.apply(this, args); 86 | } else if (isArray(ctx)) { 87 | for (var i = 0; i < ctx.length; i++) { 88 | ctx[i].apply(this, args); 89 | } 90 | } 91 | return true; 92 | }; 93 | /** 94 | *只监听一次事件,执行后立即删除 95 | * 96 | * @param {*} event 事件名称 97 | * @param {*} content 回调 98 | */ 99 | events.prototype.once = function (event, content) { 100 | if (!isFunction(content)) { 101 | throw new Error('events.prototype.once || [eventName, content] -> Error: "content" must be a function'); 102 | } 103 | this.on(event, dealOnce(this, event, content)); 104 | return this; 105 | } 106 | /** 107 | *删除某个事件的某个订阅 108 | * 109 | * @param {*} type 事件名称 110 | * @param {*} content 函数(回调函数) 111 | * @returns 当前实例 112 | */ 113 | events.prototype.removeListener = function (type, content) { 114 | var _event, ctx, index = 0; 115 | if (!isFunction(content)) { 116 | throw new Error('events.prototype.removeListener || [eventName, content] -> Error: "content" must be a function'); 117 | } 118 | _event = this.event_list; 119 | if (!_event) { 120 | return this; 121 | } else { 122 | ctx = this.event_list[type]; 123 | } 124 | if (!ctx) { 125 | return this; 126 | } 127 | if (isFunction(ctx)) { 128 | if (ctx === content) { 129 | delete _event[type]; 130 | } 131 | } else if (isArray(ctx)) { 132 | for (var i = 0; i < ctx.length; i++) { 133 | if (ctx[i] === content) { 134 | this.event_list[type].splice(i - index, 1); 135 | ctx.ListenerCount = ctx.length; 136 | if (this.event_list[type].length === 0) { 137 | delete this.event_list[type] 138 | } 139 | index++; 140 | } 141 | } 142 | } 143 | return this; 144 | } 145 | 146 | events.prototype.removeAllListener = function (type) { 147 | var _event, ctx; 148 | _event = this.event_list; 149 | if (!_event) { 150 | return this; 151 | } 152 | ctx = this.event_list[type]; 153 | if (arguments.length === 0 && (!type)) { 154 | var keys = Object.keys(this.event_list); 155 | for (var i = 0, key; i < keys.length; i++) { 156 | key = keys[i]; 157 | delete this.event_list[key]; 158 | } 159 | } 160 | if (ctx || isFunction(ctx) || isArray(ctx)) { 161 | delete this.event_list[type]; 162 | } else { 163 | return this; 164 | } 165 | } 166 | /** 167 | *获得事件类型的监听者数量 168 | * 169 | * @param {*} type 事件名/可忽略 170 | * @returns type ? 数量 ;所有事件的数量,{} 171 | */ 172 | events.prototype.getListenerCount = function (type) { 173 | var _event, ctx, ev_name = type, 174 | Count_obj = {}; 175 | _event = this.event_list; 176 | if (!_event || Object.keys(_event).length === 0) { 177 | return undefined; 178 | } 179 | if (!ev_name) { 180 | for (var attr in _event) { 181 | Count_obj[attr] = _event[attr].ListenerCount; 182 | } 183 | return Count_obj; 184 | } 185 | ctx = this.event_list[type]; 186 | if (ctx && ctx.ListenerCount) { 187 | return ctx.ListenerCount; 188 | } else { 189 | return 0; 190 | } 191 | } 192 | 193 | /** 194 | *处理 当订阅once一次事件 195 | *监听执行后删除 196 | * @param {*} target 197 | * @param {*} type 198 | * @param {*} content 199 | * @returns 200 | */ 201 | function dealOnce(target, type, content) { 202 | var fired = false; 203 | 204 | function packageFun() { 205 | this.removeListener(type, packageFun); 206 | if (!fired) { 207 | fired = true; 208 | content.apply(target, arguments); 209 | } 210 | packageFun.content = content; 211 | } 212 | return packageFun; 213 | } 214 | 215 | /** 216 | *检测是否为函数 217 | * 218 | * @param {*} fn 需要检测的函数 219 | * @returns boolean 220 | */ 221 | function isFunction(fn) { 222 | return fn instanceof Function; 223 | } 224 | /** 225 | *检测是否为对象 226 | * 227 | * @param {*} obj 检测对象 228 | * @returns boolean 229 | */ 230 | function isObject(obj) { 231 | return obj instanceof Object; 232 | } 233 | /** 234 | *检测是否为数组 235 | * 236 | * @param {*} arr 检测数组 237 | * @returns boolean 238 | */ 239 | function isArray(arr) { 240 | return arr instanceof Array; 241 | } 242 | // 适配三端 243 | if (typeof module !== 'undefined' && typeof exports === 'object') { 244 | // 兼容vue 全局 245 | var ev = factory(); 246 | module.exports = new ev(); 247 | } else if (typeof define === 'function' && (define.cmd || define.amd)) { 248 | define(factory); 249 | } else { 250 | global.Events = factory(); 251 | } 252 | })(typeof window !== 'undefined' ? window : global); 253 | --------------------------------------------------------------------------------