js函数绑定同时,如何保留代码执行环境?

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

  

 1  var EventUtil = {
 2
 3       addHandler : function(element, type, handler){
 4             if(element.addEventListener){
 5                     element.addEventListener(type, handler, false);
 6             }else if(element.attachEvent){
 7                      element.attachEvent("on"+type,handler);
 8             }else{
 9                      element["on"+type]=handler;
10             }
11        },
12         removeHandler : function(element, type, handler){
13             if(element.removeEventListener){
14                   element.removeEventListener(type, handler, false);
15            }else if(element.detachEvent){
16                    element.detachEvent("on"+type, handler);
17            }else{
18                   element["on"+type]=null;
19            }
20        }
21  } ; 

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

  像这样:

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

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

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

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

  就像这样:

 1  var handler = {
 2      info : "bind me",
 3      handleClick : function(e){
 4           alert(this.info);
 5      }
 6  }
 7  var btn = document.getElementById("mybtn");//一个按钮
 8  EventUtil.addHandler( btn, "click", function(e){
 9     handler.handleClick(e);
10  });

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

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

1  function bind(fn, context){
2      return function(){
3           return fn.apply(context, arguments);
4      }
5 }

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

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

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

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

1    var handler = {
2        info : "bind me",
3        handleClick : function(e){
4             alert(this.info+" "+e.type);
5        }
6    }
7    var btn = document.getElementById("mybtn");//一个按钮
8    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这个接口,

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

  官方对handleEvent的解释是

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

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

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

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

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

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

像这样:

 1  var handler = {
 2          info : "bind me",
 3          handleEvent : function(e){
 4               switch(e.type){
 5                      case "mousedown":
 6                            this.startHandle(e);
 7                            break;
 8                      case "mousemove":
 9                            this.moveHandle(e);
10                            break;
11                      case "mouseup":
12                            this.endHandle(e);
13                            break;
14                      default:
15                            //todo
16               }
17          },
18          startHandle:function(e){
19               //todo
20          },
21          moveHandle:function(e){
22               //todo
23          },
24          endHandle:function(e){
25               //todo
26          }
27      }
28      var btn = document.getElementById("mybtn");//一个按钮
29      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

时间: 2024-12-11 06:06:47

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

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

Chrome的扩展程序可以通过content scripts向页面中注入js代码,所注入的js代码能够对页面中所有的DOM对象进行操作.由于Chrome在js执行环境上对页面代码和content scripts代码进行了隔离,所以,在content scripts中,无法直接修改页面代码执行环境.不过我们还是可以通过一些技巧向页面代码执行环境中插入想要执行的js代码段,从而能够修改页面代码的执行环境. 第一种方法,通过在DOM对象上添加一个event handler,然后派发对应的event给该

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

有的网站已经加载了商务通.商桥的,前期定义了js函数 触发商务通.商桥代码的,可以重新定义新的函数对之前的函数进行覆盖,其 js代码为: var domain = document.domain; /*------------------加载快务通链接---------------*/ var kstJS='<script type="text/javascript" src="http://kefu6.kuaishang.cn/bs/ks.j?cI=399142&am

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

js的window.onload=function();网页加载完毕时会触发一个onload事件,这个事件与window对象相关联,是让一个函数在网页加载完毕之后得到执行.但是如果有两个韩式firstfunction和secondfunction都想在页面加载时执行,如果把它们逐一绑定到onload事件上,它们当中将只有最后那个才会被执行.由此我们可以得出一个结论:每个时间处理函数只能绑定一个指令. 解决方法一: window.onload=function(){ firstfunction()

高级函数之函数绑定

函数绑定技巧是一个非常流行的高级技巧. 函数绑定要创建一个函数,可以在特定的this环境中以制定参数调用另一个函数.该技巧常常和回调函数与实践处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境.例1如下: var handler = { message: 'Event handled', handleClick: function(e){ alert(this.message); } } var btn = document.getElementById('button'); bt

JS 执行环境与作用域链

1.执行环境 JavaScript 代码都是在执行环境中被执行的.执行环境是一个概念,一种机制,用来完成JavaScript运行时在作用域.生命周期等方面的处理,它定义了变量或函数是否有权访问其他数据,决定各自行为.每个执行环境都有一个变量对象(我理解为环境变量对象),在代码执行环境中所有的变量.函数都保存在这个对象中: 全局的执行环境是最外围的执行环境,根据ECMAScript 实现所在的宿主环境同,表示执行环境的对象也不一样,在WEB浏览器中,全局执行环境被认为是Windows对象,因此所有

JS函数、变量作用域

函数参数 函数的()中指定一个或多个形参(形式参数),多个形参之间用,号隔开,声明形参相当于在函数内部声明了对应的变量,但不赋值.在调用时在()中指定实参 ?调用时解析器不会检查实参类型.数量,实参可以是任意的数据类型. 函数返回值 使用return来设置函数的返回值,作为函数的执行结果来返回 return后可以跟任意类型的值 语法:return 值 function sum(a,b,c){ var d = a + b + c; return d; } var result = sum(1,2,

PHP代码执行漏洞

前言 最近的日子简简单单 早上起来健身+散打来一套 看看电视剧学习学习吃吃饭一天就结束了emmmm太快了一天 所以要更加努力!更加勤奋! 有的应用程序中提供了一些可以将字符串作为代码执行的函数,例如PHP中的eval函数,可以将改函数的参数当做PHP代码来执行.如果对这些函数的参数控制不严格,就可能会被攻击者利用,执行恶意代码 1. eval函数 eval函数把字符串作为PHP代码执行 <?php eval("$_POST[1]")?> 2. assert函数 assert

javascript 作用域链及闭包,AO,VO,执行环境

下面的文章内容会根据理解程度不断修正. js变量作用域: 定义:变量在它申明的函数体以及函数体内嵌套的任意函数体内有定义. function AA(){ var bb='我是AA内部变量'; function TT(){ alert(bb); } alert(bb); TT(); } AA(); 如上图,两次弹出的都是“我是AA内部变量”. JS的变量作用域是函数级的,也就是在AA内部申明的变量,在AA内部任意位置,包括它嵌套的函数内也是有定义的. 在函数AA外面,bb就是没有定义的.当然如果去

第六章 javaScript执行环境和作用域

这个只是点对于初学者其实大概了解就可以,但是要研究明白javaScript的机制,就是非常必要的,这只是我的一些记录,大家参考即可,如有错误请指出. 执行环境的概念是javaScript一个虚拟的概念,如何定义它呢?它的作用又是什么呢?它是怎么组成的呢? 大家都比较认可的说法:执行环境又称为执行上下文,从实际的表现来看,可以把它理解为由“对象”组成的一个堆栈.既然是堆栈,就是先入后出了. 组成堆栈的对象是什么对象?我没有找到确切的定义,基于我自己的理解,这个对象是一个自定义对象,里边包含有变量.