JavaScript使用自定义事件实现简单的模块化开发
WEB前端最常见驱动方式就是事件了, 所有交互等等都是通过事件,前端的常见事件有:
UI事件; 焦点事件; 鼠标事件; 滚轮事件; 文本事件; 键盘事件; 变动事件;
现在网页上有一个输入框, 如果我们要对用户输入的字符串进行过滤, 或者是截获用户的输入进行处理的话, 我们要肿么办
同学们纷纷举手说:“老师,我知道,是使用添加事件“,
老师也很欣慰地点了点头, 这群家伙真的是越来越聪明了,
老师又问了”如果要取消用户的输入怎么办了“,
坐在最前排眼睛最小的同学急忙说”ev.preventDefault(),ev.returnValue = false;"
老师又说“不错啊,都学会抢答了啊, 我裤子都脱了, 你就答这个2?”
过了一秒钟, 又一位同学主动跑到黑板这边写了如下的代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>MAKER</title> </head> <body> <div id="log"> Log: </div> <input id="text">data</input> <script> window.onload = init; if(console&&console.log) { var originalLog = console.log; var log = document.getElementById("log"); console.log = function(arg) { var eDiv = document.createElement("div"); eDiv.innerHTML = typeof arg === "string" ? arg : arg.toString(); log.appendChild( eDiv ); originalLog.apply(console, arguments); }; }; function init () { var eText = document.getElementById("text"); eText.addEventListener("keypress", function( ev ) { //逻辑全部在这里面, 很简单吧; var code = ev.which = ev.which || ev.keyCode || ev.charCode; console.log( code ); if(code > 80) { console.log("code>80"); }; if(ev.shiftKey === true) { console.log("shift key is pressed!") } }); }; </script> </body> </html>
"哎哟,不错哦,这个*x"老师说道:"这个就是我们最常用的方法, 直接为元素绑定了keypress事件,然后通过这个事件的回调对用户的输入进行处理";
如果在事件里面的代码多了, 我们又可以把事件的代码单独取出来作为一个函数:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>MAKER</title> </head> <body> <div id="log"> Log: </div> <input id="text">data</input> <script> window.onload = init; if(console&&console.log) { var originalLog = console.log; var log = document.getElementById("log"); console.log = function(arg) { var eDiv = document.createElement("div"); eDiv.innerHTML = typeof arg === "string" ? arg : arg.toString(); log.appendChild( eDiv ); originalLog.apply(console, arguments); }; }; var 我是事件 = function(ev) { //逻辑全部在这里面, 很简单吧; var code = ev.which = ev.which || ev.keyCode || ev.charCode; console.log( code ); if(code > 80) { console.log("code>80"); }; if(ev.shiftKey === true) { console.log("shift key is pressed!") }; }; function init () { var eText = document.getElementById("text"); eText.addEventListener("keypress", function( ev ) { 我是事件(ev); }); }; </script> </body> </html>
看起来清楚多了是吧, 但是在大的项目中(我说的是如果哇), 我们想要充分解耦代码, 可以事件里面的代码全部重新分开:
函数1 :console.log( code );
函数2:
if(code > 80) {
console.log("code>80");
};
函数3:
if(ev.shiftKey === true) {
console.log("shift key is pressed!")
};
好吧,现在问题来了....
现在有要上菜了哇, JS中的自定义事件, 充分利用JS的事件机制,
抄自百科:对于自定义事件最需要了解的一点是,您的代码必须导致这些事件发生。这些事件不会为响应用户或系统的动作而自动发生,即使能够编写导致事件确实以这种方式发生的代码也不例外。包含自定义事件的类模块还必须包括一个唤起事件的公有方法。这个方法通过调用 RaiseEvent 语句并传入为事件定义的任何参数来唤起事件。这些参数按顺序传入为响应事件而运行的事件过程中
自定义事件就是一种的设计模式,比如订阅者发布者模式, 观察者模式等等, 都是自定义事件基础上的模式(个人观点, 勿拍砖), 关键是很多大神写代码喜欢用这一种模式写, 那么我们也可以借鉴, 这样就够了;
我们现在把上面那段通过自定义事件的方式写出来:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>MAKER</title> </head> <body> <script> //d为目标对象, b为一个函数对象; var __extends = this.__extends || function (d, b) { //继承了静态属性 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } //继承了原型 __.prototype = b.prototype; d.prototype = new __(); }; var EventEmitter = (function () { function EventEmitter() { this._events = {}; } EventEmitter.prototype.emit = function (type) { var _this = this; var args = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { args[_i] = arguments[_i + 1]; } // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events[type] || (Array.isArray(this._events[type]) && !this._events[type].length)) { if (args[0] instanceof Error) { throw args[0]; } else { throw new Error("Uncaught, unspecified 'error' event."); } return false; } } if (this._events[type]) { this._events[type].forEach(function (handler) { return handler.apply(_this, args); }); return true; } return false; }; EventEmitter.prototype.addListener = function (type, listener) { if ('function' !== typeof listener) { throw new Error('addListener only takes instances of Function'); } var events = this._events; var listeners = events[type]; if (!listeners) listeners = events[type] = []; else if (listeners.indexOf(listener) >= 0) return this; listeners.push(listener); return this; }; EventEmitter.prototype.removeListener = function (type, listener) { var events = this._events; if (!events[type]) return this; var listeners = events[type]; var i = listeners.indexOf(listener); if (i >= 0) listeners.splice(i, 1); return this; }; return EventEmitter; })(); //写一个小的案例, 为了充分解耦代码, 我们可以把代码通过自定义的事件分成几个模块; </script> <input id="text">data</input> <script> window.onload = init; function init () { var TextEvent = (function(Emiter) { __extends(TextEvent, Emiter); function TextEvent() { Emiter.apply(this); }; return TextEvent; }.call(TextEvent,EventEmitter)); //创建了这个实例; var textEvent = new TextEvent(); //为实例添加自定义事件; textEvent.addListener("keypress", function preventDefault(ev) { ev.preventDefault(); }); //为实例添加自定义事件; textEvent.addListener("keypress", function(ev) { var code = ev.which = ev.which || ev.keyCode || ev.charCode; console.log( code ); }); //为实例添加自定义事件; textEvent.addListener("keypress", function(ev) { var code = ev.which = ev.which || ev.keyCode || ev.charCode; if(code > 80) { console.log("code>80"); }; }); //为实例添加自定义事件; textEvent.addListener("keypress", function(ev) { var code = ev.which = ev.which || ev.keyCode || ev.charCode; if(ev.shiftKey === true) { console.log("shift key is pressed!") }; }); document.getElementById("text").addEventListener("keypress", function( ev ) { textEvent.emit("keypress",ev) }); }; </script> </body> </html>
楼主你他妈在逗我? 这么多
实现要懂JS的继承:
//d为目标对象, b为一个函数对象; var __extends = this.__extends || function (d, b) { //继承了静态属性 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } //继承了原型 __.prototype = b.prototype; d.prototype = new __(); };
其次呢, 自定义事件的代码要懂, 很多地方都有用到;
var EventEmitter = (function () { function EventEmitter() { this._events = {}; } EventEmitter.prototype.emit = function (type) { var _this = this; var args = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { args[_i] = arguments[_i + 1]; } // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events[type] || (Array.isArray(this._events[type]) && !this._events[type].length)) { if (args[0] instanceof Error) { throw args[0]; } else { throw new Error("Uncaught, unspecified 'error' event."); } return false; } } if (this._events[type]) { this._events[type].forEach(function (handler) { return handler.apply(_this, args); }); return true; } return false; }; EventEmitter.prototype.addListener = function (type, listener) { if ('function' !== typeof listener) { throw new Error('addListener only takes instances of Function'); } var events = this._events; var listeners = events[type]; if (!listeners) listeners = events[type] = []; else if (listeners.indexOf(listener) >= 0) return this; listeners.push(listener); return this; }; EventEmitter.prototype.removeListener = function (type, listener) { var events = this._events; if (!events[type]) return this; var listeners = events[type]; var i = listeners.indexOf(listener); if (i >= 0) listeners.splice(i, 1); return this; }; return EventEmitter; })();
最后通过组合继承, 实现了一个自定义事件类方法:
var TextEvent = (function(Emiter) { __extends(TextEvent, Emiter); function TextEvent() { Emiter.apply(this); }; return TextEvent; }.call(TextEvent,EventEmitter));
我们通过实例化TextEvent, 为这个实例添加自定义的方法;
在onkeypress的事件回调触发自定义事件textEvent.emit("keypress",ev), 最后的最后只要为textEvent绑定自定义事件即可,这样做的优势就是: 我们可以通过removeListener去除事件, 事件函数也可以直接复用, 很灵活;
D