摘要:在所有應(yīng)用中事件處理都是非常重要的所有的均通過事件綁定到上所以大多數(shù)前端工程師需要花費很多時間來編寫和修改事件處理程序遺憾的是在誕生之初這部分內(nèi)容并未受太多重視甚至當(dāng)開發(fā)者們開始熱衷于將傳統(tǒng)的軟件架構(gòu)概念融入到里時事件綁定仍然沒有收到多大重
在所有JavaScript應(yīng)用中事件處理都是非常重要的. 所有的JavaScript均通過事件綁定到UI上, 所以大多數(shù)前端工程師需要花費很多時間來編寫和修改事件處理程序. 遺憾的是, 在JavaScript誕生之初, 這部分內(nèi)容并未受太多重視. 甚至當(dāng)開發(fā)者們開始熱衷于將傳統(tǒng)的軟件架構(gòu)概念融入到JavaScript里時, 事件綁定仍然沒有收到多大重視. 大多數(shù)事件處理相關(guān)的代碼和時間環(huán)境(對于開發(fā)者來說, 每次時間出發(fā)時才可能會用)緊緊耦合在一起, 導(dǎo)致可維護性很糟糕.
7.1 典型用法
多數(shù)開發(fā)者都很了解, 當(dāng)事件出發(fā)時, 事件對象(event 對象) 會作為回調(diào)參數(shù)傳入事件處理程序中. event對象包含所有和事件相關(guān)的信息, 包括事件的宿主(target) 以及其他和事件類型相關(guān)的數(shù)據(jù). 鼠標事件會將其位置信息暴露在event對象上, 鍵盤事件會將按鍵信息暴露在event對象上, 觸屏事件會將觸摸位置和持續(xù)事件(duration) 暴露在event對象上, 只有提供了所有這些信息, UI才會正確地執(zhí)行交互.
在很多場景中, 你只是用到了event所提供信息的一小部分, 看下面這段代碼.
// 不好的寫法 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);
這段代碼只用到了event對象的兩個屬性: clientX和 clientY. 在將元素顯示在頁面里之前先用這兩個屬性給它作定位. 盡管這段代碼看起來非常簡單且沒有什么問題, 但實際上是不好的寫法, 因為這種做法有其局限性.
7.2 規(guī)則1: 隔離應(yīng)用邏輯
上段實例代碼的第一個問題是事件處理程序包含了應(yīng)用邏輯(application logic). 應(yīng)用邏輯是和應(yīng)用相關(guān)的功能性代碼, 而不是和用戶行為相關(guān)的. 上段實例代碼中, 應(yīng)用邏輯實在特定位置顯示一個彈出框. 盡管這個交互應(yīng)當(dāng)是在用戶點擊某個元素時發(fā)生的, 但情況并不總是如此.
將應(yīng)用邏輯從所有時間處理程序中抽離出來的做法是一種最佳實踐, 應(yīng)為說不定什么事件其他地方就會觸發(fā)同一段邏輯. 比如, 有時你需要在用戶將鼠標移到某個元素上時判斷是否顯示彈出框, 或者當(dāng)按下鍵盤上的某個鍵時也作同樣的邏輯判斷. 這樣多個時間的處理程序執(zhí)行的同樣的邏輯, 而你的代碼卻被不小心賦值了多份.
將應(yīng)用邏輯放置于事件處理程序中的另一個確定是和測試有關(guān)的. 測試時需要直接出發(fā)功能代碼, 而不必通過模擬對元素點擊來出發(fā). 如果將應(yīng)用邏輯放置于事件處理程序中, 唯一的測試方法是制造事件的觸發(fā). 盡管某些測試框架可以模擬觸發(fā)事件, 但實際上這不是測試的最佳方法. 調(diào)用功能性代碼最好的做法就是單個函數(shù)調(diào)用.
你總是需要將應(yīng)用邏輯和事件處理的代碼拆分開來. 如果要對上一段實例代碼進行重構(gòu), 第一步是將處理彈出邏輯框的代碼放入一個多帶帶的函數(shù)中, 這個函數(shù)很可能掛在與為該應(yīng)用定義的一個全局對象上. 事件處理程序應(yīng)當(dāng)總是在一個相同的全局對象中, 因此就有了以下兩個辦法:
// 好的寫法 - 拆分應(yīng)用邏輯 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(evnet) { MyApplication.handleClick(event); })
之前在事件處理程序中包含了所有應(yīng)用邏輯現(xiàn)在轉(zhuǎn)移到了MyApplication.showPopup()方法中. 現(xiàn)在MyApplication.handleClick()方法制作一件事情, 即調(diào)用MyApplication.showPopup(). 若應(yīng)用邏輯被剝離出去, 對同一段功能代碼的調(diào)用可以在多點發(fā)生, 則不需要一定依賴于某個特定時間的觸發(fā), 這顯然更加方便. 單著只是拆解時間處理程序代碼的第一步.
7.3 規(guī)則2: 不要分發(fā)事件對象
在剝離出應(yīng)用邏輯之后, 上段實例代碼和存在一個問題, 即event對象被無節(jié)制地分發(fā). 它從匿名的時間處理函數(shù)傳入了MyApplication.handleClick(), 然后又傳入了MyApplication.showPopup(). 正如上文提到的, event對象上包含很多和事件相關(guān)的額外信息, 而這段代碼只用到了其中的兩個而已.
應(yīng)用邏輯不應(yīng)當(dāng)依賴于event對象來正確完成功能, 原因如下.
方法接口并沒有表明哪些數(shù)據(jù)是必要的. 好的API一定是對于期望和依賴都是透明的. 將event對象作為參數(shù)并不能告訴你event的哪些屬性時有用的, 用來干什么?
因此, 如果你想測試這個方法, 你必須重新創(chuàng)建一個event對象并將它作為參數(shù)傳入. 所以, 你需要確切的知道張哥方法使用了哪些信息, 這樣才能正切地寫出測試代碼.
這些問題在大型Web應(yīng)用中都是不可取的. 代碼不夠明晰就會導(dǎo)致bug.
最佳的辦法是讓事件處理程序使用event對象來處理事件, 然后拿到所有需要的數(shù)據(jù)傳給應(yīng)用邏輯. 例如, MyApplication.showPopup()方法只需要兩個數(shù)據(jù), x坐標和y坐標. 這樣我們將方法重寫一下, 讓它來接受這兩個參數(shù).
// 好的寫法 var MyApplication = { handleClick: function(event) { 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"; } }; addListener(element, "click", function(evnet) { MyApplication.handleClick(event); })
在這段重寫大代碼中, MyApplication.handleClick()將x坐標和y坐標傳入了MyApplication .showPopup(), 代替了之前傳入的事件對象. 可以很清晰的看到MyApplication.showPopup()所期望傳入的參數(shù), 并且在測試或代碼的任意位置都可以很輕易的直接調(diào)用這段邏輯, 比如:
MyApplication.showPopup(10, 10);
當(dāng)處理事件時, 最好讓事件處理程序成為接觸到event對象的唯一的函數(shù). 事件處理程序應(yīng)當(dāng)在進入應(yīng)用邏輯之前針對event對象執(zhí)行任何必要的操作, 包括阻止默認事件或阻止冒泡, 都應(yīng)當(dāng)直接包含在事件處理程序中, 比如:
// 好的做法 var MyApplication = { handleClick: function(event) { // 假設(shè)事件支持DOM Level2 event.preventDefault(); event.stopPropagation(); // 傳入應(yīng)用邏輯 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"; } }; addListener(element, "click", function(evnet) { MyApplication.handleClick(event); })
在這段代碼中, MyApplication.handleClick()是事件處理程序, 因此它是在將數(shù)據(jù)傳入應(yīng)用邏輯之前調(diào)用了event.preventDefault()和event.stopPropagation(), 這清楚的展示了事件處理程序和應(yīng)用邏輯之間的分工. 因為應(yīng)用邏輯不需要對event產(chǎn)生依賴, 進而在很多地方都可以輕松的使用相同的業(yè)務(wù)邏輯, 包括寫測試代碼.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89212.html
摘要:代碼無非是定義一些指令的集合讓計算機來執(zhí)行我們常常將數(shù)據(jù)傳入計算機由指令對數(shù)據(jù)進行操作并最終產(chǎn)生一個結(jié)果當(dāng)不得不修改數(shù)據(jù)時問題就來了任何時候你修改源代碼都會有引入的風(fēng)險且值修改一些數(shù)據(jù)的值也會帶來一些不必要的風(fēng)險因為數(shù)據(jù)時不應(yīng)當(dāng)影響指令的正 代碼無非是定義一些指令的集合讓計算機來執(zhí)行. 我們常常將數(shù)據(jù)傳入計算機, 由指令對數(shù)據(jù)進行操作, 并最終產(chǎn)生一個結(jié)果. 當(dāng)不得不修改數(shù)據(jù)時問題就來...
摘要:所有的塊語句都應(yīng)當(dāng)使用花括號包括花括號的對齊方式第一種風(fēng)格第二種風(fēng)格塊語句間隔第一種在語句名圓括號和左花括號之間沒有空格間隔第二種在左圓括號之前和右圓括號之后各添加一個空格第三種在左圓括號后和右圓括號前各添加一個空格我個人喜歡在右括號之后添 所有的塊語句都應(yīng)當(dāng)使用花括號, 包括: if for while do...while... try...catch...finally 3....
摘要:由于第四章太稀松平常了于是就直接跳到第五章了這里我就草草的說一下第四章的幾個點吧在嚴格模式的應(yīng)用下不推薦將用在全局作用域中相等推薦盡量使用和守則如果是在沒有別的方法來完成當(dāng)前任務(wù)這時可以使用原始包裝類型不推薦創(chuàng)建類型時用等創(chuàng)建類型從這一章節(jié) 由于第四章太稀松平常了, 于是就直接跳到第五章了.這里我就草草的說一下第四章的幾個點吧 在嚴格模式的應(yīng)用下 不推薦將use strict;用在全...
摘要:注釋是代碼中最常見的組成部分它們是另一種形式的文檔也是程序員最后才舍得花時間去寫的但是對于代碼的總體可維護性而言注釋是非常重要的一環(huán)打開一個沒有任何注釋的文件就好像趣味冒險但如果給你的時間有限這項任務(wù)就變成了折磨適度的添加注釋可以解釋說明代 注釋是代碼中最常見的組成部分.它們是另一種形式的文檔,也是程序員最后才舍得花時間去寫的.但是,對于代碼的總體可維護性而言,注釋是非常重要的一環(huán).打...
摘要:程序是寫給人讀的只是偶爾讓計算機執(zhí)行一下當(dāng)你剛剛組建一個團隊時團隊中的每個人都各自有一套編程習(xí)慣畢竟每個成員都有著不同的背景有些人可能來自某個皮包公司身兼數(shù)職在公司里面什么事都做還有些人會來自不同的團隊對某種特定的做事風(fēng)格情有獨鐘或恨之入骨 程序是寫給人讀的,只是偶爾讓計算機執(zhí)行一下. Donald Knuth 當(dāng)你剛剛組建一個團隊時,團隊中的每個人都各自有一套編程習(xí)慣.畢竟,...
閱讀 1185·2021-09-27 13:34
閱讀 995·2021-09-13 10:25
閱讀 521·2019-08-30 15:52
閱讀 3460·2019-08-30 13:48
閱讀 660·2019-08-30 11:07
閱讀 2181·2019-08-29 16:23
閱讀 2007·2019-08-29 13:51
閱讀 2340·2019-08-26 17:42