经常写js的程序员一定不会对下面这段代码感到陌生。

  

  var EventUtil = {

       addHandler : function(element, type, handler){
if(element.addEventListener){
element.addEventListener(type, handler, false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
removeHandler : function(element, type, handler){
if(element.removeEventListener){
element.removeEventListener(type, handler, false);
}else if(element.detachEvent){
element.detachEvent("on"+type, handler);
}else{
element["on"+type]=null;
}
}
} ;

  这段代码可以用跨浏览器的方式给元素绑定事件处理函数。

  像这样:

 var handler = {
info : "bind me",
handleClick : function(e){
alert(this.info);
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "click", handler.handleClick);

  这样做的话,alert框中显示的不是“bind me”,而是undefined。这是因为没有保存handler.handleClick的环境,即handleClick 中的this并没有指向handler类。

  一般情况下,这样做也没有什么问题。但在某些需要保存代码的执行环境的情况下,比如说,事件处理函数、setTimeout()等。在这些情况下,我们可能会需要利用原来执行环境中的某些变量,状态标识等信息。

  比较容易想到的一种解决方法是使用闭包。

  就像这样:

  var handler = {
info : "bind me",
handleClick : function(e){
alert(this.info);
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "click", function(e){
handler.handleClick(e);
});

   但是,闭包用多了会让代码变得难以维护。

  于是翻阅资料,发现一个常用的函数

  function bind(fn, context){
return function(){
return fn.apply(context, arguments);
}
}

  是的,这个函数也使用了闭包,但是这个bind函数还是很强大的,它可以将我们的函数fn,绑定到我们制定的执行环境context中。这个bind函数还是比较好理解的。那么,可以这样来使用它。

   var handler = {
info : "bind me",
handleClick : function(e){
alert(this.info);
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "click", bind(handler.handleClick, handler));

  注意这句 fn.apply(context, arguments); 这样可以保证事件对象e也传递给了fn。

  令人庆幸的是,在ECMAScript5中,所有函数都定义一个原生的bind函数,我们可以直接来使用它。

    var handler = {
info : "bind me",
handleClick : function(e){
alert(this.info+" "+e.type);
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "click", handler.handleClick.bind(handler));

  值得注意的是,IE9+、ff4+,chrome支持这个bind函数。

  最近在看一个开源项目的源码时,发现了一个更好的方法,这个方法不仅能解决上述问题,还能提供更好的事件管理方案。

  这个方法就是

addEventListener + handleEvent

首先,我们来看看addEventListener的参数都有哪些,

parameters:

    1. type  of type DOMString

      The event type for which the user is registering

      用户注册的事件类型

    2.listener of type EventListener

      The listener parameter takes an interface implemented by the user which contains the methods to be called when the event occurs.

      listener是用户定义的一个EventListener接口的实现,事件发生时会被调用。(这里是重点)

    3.useCapture of type boolean

      定义事件在哪个阶段被处理,一般都是false,即在冒泡阶段处理事件。

再来看看,EventListener这个接口,

 // Introduced in DOM Level 2:
interface EventListener {
void handleEvent(in Event evt);
};

  官方对handleEvent的解释是

  This method is called whenever an event occurs of the type for which the EventListener interface was registered.  

  就是说,在事件发生被处理时,这个函数会被调用,evt就是事件对象。

  看来addEventListener的第二个参数,不仅支持传入一个回调函数,还可以传入一个对象,前提是这个对象中实现了handleEvent方法。利用这个特性,可以这样来使用:

     var handler = {
info : "bind me",
handleEvent : function(e){
alert(this.info+" "+e.type);
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "click", handler);

  这样一来,不用当心函数的执行环境被改变。

  这个方法还有一个更大的用处就是可以创作一个事件管理中心来更方便,更优雅地管理事件。

像这样:

  var handler = {
info : "bind me",
handleEvent : function(e){
switch(e.type){
case "mousedown":
this.startHandle(e);
break;
case "mousemove":
this.moveHandle(e);
break;
case "mouseup":
this.endHandle(e);
break;
default:
//todo
}
},
startHandle:function(e){
//todo
},
moveHandle:function(e){
//todo
},
endHandle:function(e){
//todo
}
}
var btn = document.getElementById("mybtn");//一个按钮
EventUtil.addHandler( btn, "mousedown", handler);

  handler类中的handleEvent方法就是一个事件中心,通过事件的类型,来决定采用哪种处理逻辑。是不是很方便。

  注意:这个接口属于DOM2,所以只能在IE9+,ff,chrome等浏览器中使用。

参考资料

  1.《JavaScript高级程序设计》

  2.http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventListener

  3.http://www.tuicool.com/articles/uIfeM3V

js函数绑定同时,如何保留代码执行环境?的更多相关文章

  1. Chrome扩展修改页面代码执行环境的方法

    Chrome的扩展程序可以通过content scripts向页面中注入js代码,所注入的js代码能够对页面中所有的DOM对象进行操作.由于Chrome在js执行环境上对页面代码和content sc ...

  2. js函数和变量的声明与执行顺序

    一.函数执行顺序 1.正常顺序 function f(){ alert(2); } f(); //alert 2 所有浏览器都能测试通过. 2.倒序调用 f(); //alert 2 function ...

  3. 网站加载有商务通、商桥,定义js函数触发快商通代码

    有的网站已经加载了商务通.商桥的,前期定义了js函数 触发商务通.商桥代码的,可以重新定义新的函数对之前的函数进行覆盖,其 js代码为: var domain = document.domain; / ...

  4. 把多个js函数绑定到onload时间处理函数上

    js的window.onload=function();网页加载完毕时会触发一个onload事件,这个事件与window对象相关联,是让一个函数在网页加载完毕之后得到执行.但是如果有两个韩式first ...

  5. preg_replace函数/e 模式下的代码执行+一道例题

    目录 例一 例二 补充 看一道ctf题-----[BJDCTF2020]ZJCTF,不过如此 参考链接 例一 源码: <?php preg_replace('/(.*)/ei', 'strtol ...

  6. JS函数 编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数。

    编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数. 任务 第一步: 编写代码完成一个函数的定义吧. 第二步: 我们来补充函数体中的控制语句,完成函数功能吧. 提示: ...

  7. js 高程 22.1.4 函数绑定 bind() 封装分析

    js 高程 书中原话(斜体表示): 22.1.4 函数绑定 另一个日益流行的高级技巧叫做函数绑定.函数绑定要创建一个函数,可以在特定的this 环境中 以指定参数调用另一个函数.该技巧常常和回调函数与 ...

  8. JavaScript函数绑定

    一个简单的函数绑定 在JavaScript与DOM交互中经常需要使用函数绑定,定义一个函数然后将其绑定到特定DOM元素或集合的某个事件触发程序上,绑定函数经常和回调函数及事件处理程序一起使用,以便把函 ...

  9. js函数和变量的执行顺序【易错】

    js函数和变量的声明与执行顺序 一.函数执行顺序 1.正常顺序 function f(){ alert(2); } f(); //alert 2 所有浏览器都能测试通过. 2.倒序调用 f(); // ...

随机推荐

  1. SQL中的去重操作

    if not object_id('Tempdb..#T') is null drop table #T Go Create table #T([ID] ),[Memo] nvarchar()) In ...

  2. Android开发问题汇总(持续更新)

    在Android开发中,总会有一些很小的问题.由于我们的不仔细,很容易忽略掉,从而导致在该问题上花费了很多的时间,造成工作进度的延迟. 为此,在这里做一下记录,避免再次浪费许多时间在这些问题上. 1. ...

  3. eclipse中输入的中文为繁体的问题

    今天在eclipse中编写注释的时候发现,输入的中文都为繁体,且只在eclipse编辑器中为繁体,切换到网页中则为正常. 最后发现,竟然是输入法的shift+ctrl+F快捷键和eclipse的冲突. ...

  4. Phalcon 性能最高的php框架没有之一

    最近有个朋友说他们现在在用Phalcon框架开发项目,明显感觉性能有很大的提升,所有着手学习下这个高性能框架,网上是这样说的“虽然以C语言编写达到较高的效能,但相对的一项缺点就是在服务器上必须拥有管理 ...

  5. MFC网络编程

    一.概念1.同步方式与异步方式同步方式:发送方不等接收方响应,便接着发送下一个数据包的通信方式异步方式:发送方发出数据,等收到接收方发回的响应后,才发送下一个数据包的通信方式2.阻塞与非阻塞方式阻塞套 ...

  6. 关于MySQL MyISAM 表并发

    MyISAM的锁调度 MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的.那么,一个进程请求某个MyISAM表的读锁,同时另一个进程也请求同一表的写锁,MySQL如何处理呢?答案是写进程先获得 ...

  7. nginx代理配置

    server {     listen       80;     server_name  api.colortrip.cn;     client_max_body_size 10m;     a ...

  8. 安装虚拟机VMWare时出现1021错误的解决办法

    今天安装虚拟机(VMWare Workstation9.0),中途老是出现错误:Failed to create the requested registry key key installer er ...

  9. ParseChat应用源码ios版

    ParseChat是一个完全原生的iPhone应用程序,用于创建实时的.基于文本的Parse聊天室.功能:支持多台设备之间的实时聊天,可动态添加新的聊天室,支持基本配置,可发送和接收音效以及任意大小的 ...

  10. spoj BRCKTS - Brackets 线段树

    题目链接 给一个括号序列, 两种操作. 一种将某个位置的括号变反(左变右, 右变左), 第二种是询问这个括号序列是否合法. 线段树, 我们开两个数组lf, rg. 表示某个区间里面, 右边的左括号个数 ...