读《编写可维护的JavaScript》第七章总结

 

第七章 事件处理

7.1 典型用法

作者首先给了个我们一个处理事件的方法。看起来也没啥俩样,不过后来给出的优化方法很值得学习:

         // 不好的写法
         function handleClick(event) {
            var popup = document.getElementById("popup");
            popup.style.left = event.clientX + "px";
            popup.style.top = event.clientY + "px";
            pop.className = "reveal";
         }
         
         // 一个事件绑定事件(第5章的)
         addListener(element, "click", handleClick);

作者补充了一句:这段代码只用到了event对象的俩个属性:clientX和clientY。在将元素显示在页面里之前先用这俩个属性给它做定位。

                       尽管这段代码开起来非常简单且没有什么问题,但实际上是不好的写法,因为这种做法有其局限性。

 7.2 规则 1:隔离应用逻辑

首先,这段代码的应用逻辑(application logic)和用户行为混在一起了。就是说:这个事件的处理方法和点击事件连在一起了,从而导致耦合度的提高。

这个情况其他语言中也很常见,通常我们将处理逻辑封装起来从而可以把代码重用,所以作者提出了很重要的思想:

将应用逻辑从所有事件处理程序中抽离出来的做法是一种最佳实践。

 将代码重构一下变成这样:

         // 好的写法 - 拆分应用逻辑
         var MyApplication = {
             handleClick: function(event){
                   this.showPopUp(event);
             },

             showPopUp: function(event) {
                  var popup = document.getElementById("popup");
                  popup.style.left = event.clientX + "px";
                  popup.style.top = event.clientY + "px";
                  popup.className = "reveal";
             }
         };
         
         addListener(element, "click", function(event) {
                 MyApplication.handleClick(event);
         });

 

7.3 规则2:不要分发事件对象

上述代码还存在一个问题:即event对象被无节制地分发。event对象上包含很多的和事件相关的额外信息,而这段代码只用到了其中俩个而已。

作者提供了俩个理由:

  1. 方法接口并没有表明那些数据是必要的。好的API一定是对于期望和依赖都是透明的。(译者在此也加了译注:大多数情况并不理想,主要看第二点)。
  2. 方便测试

最佳的办法是让事件处理程序使用event对象来处理事件,然后拿到所有需要的数据传给应用逻辑。

将代码重构一下:

         // 好的写法
         var MyApplication = {

             handleClick: function(event){
                   this.showPopUp(event.clientX,event.clientY); // 这里标明所期待传入的参数
             },

             showPopUp: function(x,y) { // 这里的参数就不再依赖event咯
                  var popup = document.getElementById("popup");
                  popup.style.left = x + "px"; 
                  popup.style.top = y + "px";
                  popup.className = "reveal";
             }
         };

         addListener(element, "click", function(event) {
                 MyApplication.handleClick(event);
         });

测试时或者代码的任意位置都很轻易调用这段逻辑:

         // 这样调用非常棒
         MyApplication.showPopUp(10, 10);

最后,当处理事件时,最好让事件处理程序成为接触到event对象的唯一的函数。事件处理程序应当在进入应用逻辑之前针对event对象执行任何必要的操作,包括阻止默认事件或阻止事件冒泡,都应当直接包含在事件程序中。

         // 好的写法
         var MyApplication = {

             handleClick: function(event){

                   // 假设事件支持DOM Level2
                   event.preventDefault();
                   event.stopPropagation();

                   // 传入应用逻辑
                   this.showPopUp(event.clientX,event.clientY);
             },

             showPopUp: function(x,y) {
                  var popup = document.getElementById("popup");
                  popup.style.left = x + "px";
                  popup.style.top = y + "px";
                  popup.className = "reveal";
             }
         };

在这段代码中,MyApplication.handleClick()是事件处理程序,因此它在数据传入应用逻辑之前调用 event.preventDefault()和 event.stopPropagation(),这清楚的展示了事件处理程序和应用逻辑之间的分工。因为应用逻辑不需要对event产生依赖,进而在很多地方都可以轻松地使用相同的业务逻辑,包括写测试代码。

原文地址:https://www.cnblogs.com/xhy-steve/p/5132793.html