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