原文:http://bbs.ruoren.com/thread-36873994-1-1.html
************************************************************************
前言
今天分享的這篇文章比較適合js初學者,是由堅持閱讀分享第一期的兩位童鞋翻譯與校對,他們分別是@安生與@墨白。整篇內容從模組、代碼審查以及最後的測試上都做了介紹。
本文從這開始~
JavaScripts是一種易學難精的程式設計語言。然而很多文章的內容都是假設你已經熟練掌握了js編程技巧。
在1995年JavaScript作為LiveScript面世時,我就開始使用它了,但後來我逐漸從用戶端開發人員,轉到服務端安全的領域。。在過去的五年裡,我重新關注用戶端開發。我驚喜地發現瀏覽器功能更加強大,也更容易調試。但JavaScript開始變得複雜且不易掌握。最近我轉變了自己的心態,沒必要去精通JavaScript,但我可以選擇讓自己js編寫水平更好。我很樂於成為一個“好”的JavaScript開發人員。
下面是一些我認為在編寫JS代碼方面很實用的技巧和技術,比如‘程式碼群組織’、‘代碼檢查’、‘測試’以及‘使用瀏覽器開發人員工具’。其中一些對於有經驗的JS開發人員來說是很簡單的事,但卻是新手非常容易養成的壞習慣。這些規範已經協助我提高了我的技能,也給我的使用者帶來更好的體驗。這不正是我們的首要目標嗎。
組織
作為一個JS開發新人,不可避免地會在HTML頁面頂部放上一堆代碼。開始總是很簡單,一段簡單自動對焦到表單欄位的jQuery,接著可能就是表單驗證,然後是網頁中常見的彈出框——就是那種會中斷人們閱讀內容,以便FaceBook表示他們喜歡這個網站。經過一些迭代後就會變成幾百行的JS代碼,淹沒在有幾百行標記的HTML文檔裡面。
這簡直一團糟,所以不要這樣做。這個聽起來很簡單,以至於我都認為不需要把它寫下來。但是,當你匆匆忙忙在趕一個快速指令碼時,會非常容易在頁面的頂部形成堆積。為了避免這種情況,當你在製作一個新的網站時養成建立一個JS文檔的習慣。同時引用script指令碼標籤,之後你就可以方便地添加互動或其他用戶端的特性了。
一旦你離開HTML頁面(有沒有感覺變得更加簡潔。),下一步就要組織代碼了。那些幾百行的JS代碼可能能夠運作,但你在幾個月後再次調試或修改時,你會發現你只是想單純找到一段函數而已。
所以如果簡單地從HTML中移動一些js代碼到其他文檔還不夠,下一步該做什麼呢?
架構
很明顯架構是解決之道。把所有東西搬到AngularJS、Ember、React或者其他。重建整個網站成單頁面應用或者MVC會令人抓狂的。
或者並不需要。不要誤解,當我做App時我喜歡用Angular,但app和網頁之間有互動性方面的區別。Ajax-enhancedproduct catalog和Gmail也有區別——至少幾百行的代碼不同。但如果你不打算使用架構,會有什麼其他的選擇呢?
設計模式
關於設計模式有一個說法就是“這是可以人們處理過去遇到的問題的經驗總結。”它們肯定是有用的。Addy Osmani寫了一本關於這個主題的好書——《學習JS設計模式》,你可以免費下載和閱讀我推薦的這本書。但關於這本書我有一個問題(以及對於這話題類似的討論),你可以看到像這樣的代碼:
理論上,設計模式對我來說是有意義的,但實際上並不。很難把它應用在一個實際的網頁上下文環境中。
模組
在所有我閱讀過的模式中,我覺得模組模式是最簡單以及最易適用於在現有代碼中的。
高層次的模組模式會用一組代碼簡單地創造一個閉包。你可以用一個相關的集合函數,把它們放進一個模組,然後選擇你想要公開的介面。這將建立更容易在其他項目的應用的“黑盒子”代碼。你也可以在模組中將代碼移到不同的文檔。
來看一個關於模組模式的簡單例子。我知道剛開頭會有個文法看起來很奇怪,不用著急,讓我們先來看下這個“容器”,我會解釋不同的點的:
會對這些圓括弧感到疑惑嗎。老實說,儘管我瞭解JavaScript,我仍不是容易轉過圈來想明白它們在做些什麼,於是,我換了一種思考方式,從代碼塊裡面開始閱讀代碼。
從一個簡單的函數開始,在這裡面定義的方法可以在模組中調用.
最後一對括弧會自動運行函數。我們會返回函數中定義的東西,當然現在它還是空的。但是高亮顯示的JavaScript代碼現在是無效的。那麼如何讓它有效。
function{}裡面的圓括弧使得JS語句變得有效。不信可以開啟你的開發人員工具控制台然後嘗試自己輸入。
回到一開始……
最後一件要做的事是把函數自動啟動並執行結果賦給一個變數。儘管我個人完全明白這段代碼的含義,但每次我看到這段代碼我都會停下來,稍微提醒自己這段代碼的含義。不好意思提起,我在編輯器中保持著這段代碼,這樣我就可以複製和粘貼這段“空”模組來進行快速複用。
既然我們已經明白了這種有點奇怪的文法,那麼真正的模組模式是什麼樣子的呢。
前邊的代碼建立了一個叫做counterModule的模組。它包括兩個函數:incrementCounter和resetCounter。運行可以看到:
想法就是打包好在counterModule裡面的所有這些代碼。打包是入門級的東西,未來的JS會提供更加簡單的方法來做這些事情,但現在而言我發現模組模式非常簡單,它是一種處理組織問題的簡單實用的方法。
一個實用的模組例子
我們來構建一個可以匹配真實情境的簡單例子。我儘可能簡單地講,同時會有一些你可能會在實際的web應用中遇到的情境。
假設你的網路遊戲公司Lyntendo,有一個使用者註冊入口讓使用者建立遊戲身份。你需要構建一個可供使用者選擇名字的表格。對於身份規則有些要求:
使用者名稱必須用大寫字母開頭。
使用者名稱必須要兩個字母及以上的長度。
允許空格,但不能有標點符號。
使用者名稱不能包括非主流詞彙。
首先,我們來寫一個非常簡單的表格。
以上提供了表格以及提交按鈕,同時也引進了描述規則的js檔案,現在讓我們來看一下代碼。
從底部開始,我用一些基本代碼來擷取頁面中的元素(在這我沒有用jQuery),然後監聽按鈕的click事件。擷取使用者輸入的值然後把它傳到我寫的驗證方法中去。驗證輸入的值是否符合上面我描述的規則。代碼不太複雜,但當我的驗證規則增加並且我想在頁面中加入其它互動的點時,代碼就會變得複雜了。讓我們來重寫下這個模組。
首先,建立一個新的js檔案叫game.js,接著通過script標籤引用在我的index.html。然後我把描述驗證邏輯的內容放到一個模組中。
這和之前並沒有太大區別,但現在它已打包好了放在gameMoudule的變數中,並且它有一個有效API。現在看下app.js。
相比於之前的代碼,可以看到少混合了很多關於DOM監聽的代碼。所有驗證的功能(兩個函數和一個不符合驗證規則的壞詞列表)現在安全地放在模組中,讓代碼更容易工作。在你的編輯器,你也可以完成代碼的模組方法。
代碼模組化不是必要和複雜的事,但它更簡潔和簡單,是非常好的東西。
代碼檢查
代碼檢查適用於在練習或其他問題中來檢查你的代碼。
你記得那些你重新命名的函數然後提醒自己你會待會修補它的時候嗎?
你記得當你定義一個函數去接收兩個參數但最後卻從頭到尾只使用了一個的時候嗎?
你記得有時寫過一些非常愚蠢的代碼嗎。那些不能啟動並執行,比如fuction和functon。
代碼檢查可以幫上忙。對於所有人來說,代碼檢查不只是好的練習,也可以是文法和基本的邏輯檢查。而且,利用代碼檢查工具協助我們改正“我知道怎麼修改代碼,等我有時間的時候再去修改代碼”這樣的壞習慣。所以,很多現代編輯器都有代碼檢查外掛程式。我現在的編輯器(Sublime,Brackets,Visual Studio Code),所有都支援提供代碼檢測即時反饋。
舉個例子,這是一份VisualStudio Code的報告。我故意把一些代碼寫得很爛。
上圖Visual Studio Code報了我代碼中的一些錯誤。VSC的linter,和大多數的linters一樣,會提供關於你關心什麼和你會認為什麼是錯誤(“必須修複”)與警告(“別懶惰了,趕緊修複吧”)之間的選項。
如果你不想要安裝任何東西或配置你的編輯器,可以在JSHint.com進行線上代碼檢查。JSHint可能是最受歡迎的linter,它基於另一個linter:JSLint(兩者是不同的)。JSHint比JSLint嚴格的多。你可以直接在編輯器或通過命令列使用JSHint,當然,最簡單的使用方法就是在網頁上嘗試一下。
左邊的代碼是即時的編輯,右邊是關於左邊代碼的即時報告。簡單在代碼中引入一個低級錯誤就能大概清楚流程了。我把main函數改成main2:
網站立刻就報了兩個錯誤。但這些並不是語法錯誤。似乎上面的代碼看起來沒有問題,但JSHint注意到了你可能沒意識到的問題(這隻是一個5行的代碼塊,但想象一下在一個偌大的文檔中到處都是函數而且被調用的函數分散在許多行中,人工檢查代碼會瘋的)。
那麼實際的例子如何,下面的代碼中(現在使用jQuery了),是一些簡單的JS處理表單驗證。這種瑣碎的事,可能現在一半的JS代碼都在做(還有突然出現要求你點擊“喜歡”這個網站的模態框)。
這段代碼開頭寫了兩個函數來協助驗證年齡和郵箱。然後我們有一個document.ready代碼塊來監聽表格提交。擷取來自三個地區的值,檢查是否空白(或無效),然後警告表格無效或者繼續執行(或者在我們的例子中,表格就這樣了)。
我們把這些放進JSHint然後看看可以獲得什麼:
天呐,有好多問題,但是相似的問題發生了很多次,剛開始使用linters時是這樣。我並沒有特意製造許多獨特的錯誤,只是重複了許多相同的錯誤。第一個非常簡單——使用嚴格相等“===”來取代“==”。這裡嚴格測試值是否是Null 字元串。好吧,我們先來修複它。
下面是JSHint裡面更新的報告:
現在清楚了。下一個塊是關於“未定義的變數”。這看起來很奇怪,如果你使用過jQuery的話,你就會知道'$'。badForm存在的問題很簡單——我忘記用var給他定義範圍。但我們如何修複$呢。JSHint提供一個方法去配置代碼怎樣去檢查出問題。通過新增內容(類似注釋)到我們的代碼,我們可以讓JSHint知道$變數是作為全域存在的而且可以放心使用的。
然後是JSHint的更新報告:
啊,終於快完了。最後問題是一個很好的例子,這個例子說明了JSHint可以提供除了代碼錯誤之外的資訊。在這個例子中,我忘寫了一個處理年齡核實的函數。我已經建立了validAge,但在表格檢查地區,並沒有調用它。函數雖然只有一行,但可以以防後面驗證更繁雜,所以我決定保留它。這是最終的代碼版本(demo_jshint/app.js)
這個版本最終“通過了”JSHint的測試。這個代碼還不是完美的。現在有validAge和invalidEmial這兩個驗證函式。如果通過驗證的話,兩個驗證函式的傳回值不同,一個是true,一個會是false。如果傳回值是一致的會更好。同樣認識到每次驗證運行,jQuery都會被擷取來自DOM的三個節點。