《编写可维护的JavaScript》读书笔记之编程实践-事件处理

事件处理

在所有 JavaScript 应用中事件处理都是非常重要的。所有的 JavaScript 均通过事件绑定到 UI 上,所以大多数前端工程师需要花费很多时间来编写和修改事件处理程序。由于事件绑定没有受到多大重视,大多数事件处理相关的代码和事件环境(对于开发者来说,每次事件触发时才会可用)紧紧耦合在一起,导致可维护性很糟糕。

典型用法

【事件对象】:当事件触发时,事件对象(event 对象)会作为回调参数传入事件处理程序中。event 对象包含所有和事件相关的信息,包括宿主(target)以及其他和事件类型相关的数据。例如,鼠标事件会将其位置信息暴露在 event 对象上,键盘事件会将按键的信息暴露在 event 对象上,触屏事件会将触摸位置和持续时间(duration)暴露在 event 对象上。只有提供了所有这些信息,UI 才会正确地执行交互。

【情境】:

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

规则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);
});

【说明】:将应用逻辑剥离出去,对同一段功能代码的调用可以在多点发生,则不需要一定依赖于某个特定事件的触发,这显然更加方便。

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

上例情境代码还存在一个问题:event 对象被无节制地分发。从匿名的事件处理函数传入了 MyApplication.handleClick(),然后又传入了 MyApplication.showPopup()。event 对象上包含很多和事件相关的额外信息,而这段代码只用到了其中的两个而已。

【原则】:应用逻辑不应当依赖于 event 对象来正确完成功能。

  • 方法接口并没有表明哪些数是必要的。好的 API一定是对于期望和依赖都是透明的。将 event 对象作为参数并不能告诉你 event 的哪些属性是有用的,用来干什么?(译者:作者的意思是说好的 API 要更明确清楚,但这个观点我们需要辩证地对待,如果明确知道回调传值的用处以及需要传哪些值,当然更好。但更多的时候我们并不知道应用逻辑做何种事情,因此需要为应用逻辑提供尽可能多的信息,如何利用这些信息,效率如何统统交由应用逻辑负责,以达到某种层次的解耦。)
  • 如果你想测试这个方法,必须重新创建一个 event 对象并将它作为参数传入。所以,需要确切地知道这个方法使用了哪些信息,这样才能正确地写出测试代码。

【注意】:接口格式不清晰和自行构造 event 对象来用于测试在大型 Web 应用中都是不可取的。代码不够清晰就会导致 bug。

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

【重构】:

var MyApplication = {
    handleClick: function(event) {
        this.showwPopup(event.clientX, event.clientY);
    },
    
    showPopup: funciton(clientX, clientY) {
        var popup = document.getElementById('popup');
        popup.style.left = clientX + "px";
        popup.style.top = clientY + "px";
        popup.className = "reveal";
    }
};

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

// 测试应用逻辑代码时,不需要自行构造 event 对象。
MyApplication.showPopup(10, 10);

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

var MyApplication = {
    handleClick: function(event) {
    
        // 假设事件支持 DOM Level2
        event.preventDefault();
        event.stopPropagation();
    
        // 传入应用逻辑
        this.showwPopup(event.clientX, event.clientY);
    },
    
    showPopup: funciton(clientX, clientY) {
        var popup = document.getElementById('popup');
        popup.style.left = clientX + "px";
        popup.style.top = clientY + "px";
        popup.className = "reveal";
    }
};

addListener(element, "click", function(event) {
    MyApplication.handleClick(event);
});
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,470评论 1 11
  • 0. 写在前面 当你开始工作时,你不是在给你自己写代码,而是为后来人写代码。 —— Nichloas C. Zak...
    康斌阅读 5,296评论 1 42
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,096评论 0 21
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,857评论 6 13
  • 举酒东篱下,豁然见远山, 君品绝而逸,我歌雅而贤, 七弦恰成韵,壶里小悲欢, 人生无穷己,皆归茶梦谈。
    雲灬阅读 197评论 0 1