使用 JSLint 保證 JavaScript 代碼質量

簡介: 隨着富 Web 前端應用的出現,開發人員不得不重新審視並重視 JavaScript 語言的能力和使用,拋棄過去那種只靠“複製 / 粘貼”常用腳本完成簡單前端任務的模式。JavaScript 語言本身是一種弱類型腳本語言,具有相對於 C++ 或 Java 語言更爲鬆散的限制,一切以函數爲中心的函數式編程思想也爲開發人員提供了更加靈活的語法實現。然而,這種靈活性在帶來高效的同時,也成爲初學或者經驗不足的 JavaScript 開發人員的噩夢。形式各異的代碼風格、隱含錯誤的代碼行爲,嚴重影響了整體代碼的可讀性和穩定性,成爲 Web 項目中最爲常見問題之一。

因而,我們需要一個有效的 JavaScript 代碼質量工具,以便能及時發現並修正 JavaScript 代碼中所隱含的問題,保證代碼交付質量。JSLint 作爲一個靈活有效的 JavaScript 代碼質量檢測工具,允許使用者指定滿足其特定應用開發需求的編碼風格約定,使得整個項目的風格統一,這種“規則”(options)驅動的工作方式使得 JSLint 能夠適用於不同的代碼檢測需求。本文將首先向讀者介紹 JSLint 的基本概念和作用,講解其基於規則的工作方式,而後通過一個示例闡明其基本的使用方法,最後介紹如何將 JSLint 整合到 Ant 和 Eclipse 的應用過程,以全方面展示如何將 JSLint 在日常開發任務中加以運用。

什麼是 JSLint

JavaScript 作爲一門年輕、語法靈活多變且對格式要求相對鬆散的語言,代碼格式的混亂和某些語言特性的不正確使用,往往使得最終交付的產品中包含許多因編碼風格約定造成的未預見的行爲或錯誤,這種習慣性的問題如果不及時指出並修改,往往會在項目的迭代過程中不斷的重現,嚴重影響 Web 產品的穩定性與安全性。JSLint 正是 Douglas Crockford 同學爲解決此類問題創建的工具,JSLint 除了能指出這些不合理的約定,還能標出結構方面的問題。雖然 JSLint 不能保證代碼邏輯一定正確,但卻有助於發現錯誤並教會開發人員一些好的編碼實踐。值得一提的是 JSLint 工具本身也是一段 JavaScript 代碼,它是檢驗 JavaScript 代碼質量的 JavaScript 腳本。JSLint 對 JavaScript 腳本的質量檢測主要包括以下幾個方面:

  • 檢測語法錯誤:例如大括號“{}”的配對錯誤。
  • 變量定義規範:例如未定義變量的檢測。
  • 代碼格式規範:例如句末分號的缺失。
  • 蹩腳語言特性的使用檢測:如 eval 和 with 的使用限制。

JSLint 的版本更新一直處於活躍狀態,截至本文撰寫之時,JSLint 最新版本的發佈時間爲 2010-10-10。很多主流代碼編輯器均對 JSLint 提供了良好的擴展支持,包括 Eclipse、VS2008 等等。

目前,與 JSLint 功能類似的 JavaScript 代碼檢測工具有很多,包括:YUI Test、Firebug、MS Script Debugger 、CompanionJS 等等,它們中大多數都是以瀏覽器插件的形式存在於客戶端瀏覽器進行 JavaScript 運行時的檢測和調試,JSLint 與這些工具的重要區別在於其更加註重靜態代碼格式的檢測,而這也正是當前火熱的敏捷開發中持續構建所需要和提倡的。

認識 JSLint 規則

JSLint 執行代碼質量檢測的原理核心在於用戶設定的規則集。JSLint 默認提供的規則集包含了 Web 開發人員多年積累下來的認爲不好的開發風格,我們可以根據自己項目的需求選擇構建一套特定的規則。JSLint 將根據它進行對 JavaScript 腳本的掃描工作,並給出相應的問題描述信息。規則的形式體現爲多組鍵值對:[param:option],以規則名做鍵,對規則調用與否做值。例如規則:“plusplus:true”是不允許 ++ 和 -- 運算符的出現,“undef:true”是不允許使用未定義的變量。

由於 JSLint 工具本質上是一個普通的 JS 腳本,其運行也自然依賴於一個 JS 運行引擎,其被引擎加載後會在內存中產生一個全局 JSLint 函數對象,該函數對象需要兩個輸入量:source 和 options,前者用來指定待檢測的腳本文件被解析後生成的字符串或字符串數組,後者則表示用戶自定義的規則選項。若 options 爲空,JSLint 則使用其默認的規則對 source 進行掃描檢測。

整個檢測過程就是對腳本中所含 JSLINT (source, options) 函數的一次執行過程。當指定的 source 腳本在 options 條件下檢測通過,則 JSLint 返回 true,否則返回 false,而這時則可以通過 JSLINT.errors 對象獲得詳細的錯誤信息。圖 1 展示了 JSLint 的整個工作過程。


圖 1. JSLint 工作過程示意圖
圖 1. JSLint 工作過程示意圖 

如圖所示,規則集的配置方式有三種:

  1. 直接通過修改 JSLint.js 源碼來修改默認規則。
  2. 在 JSLint 函數運行時,同時設置 options 參數,動態改變其規則選項(first overwrite)。此方式適用於對批量 js 文件使用同樣的一組自定義規則。
  3. 通過在待檢測的 js 文件頭部添加註釋類型的規則,對單個 js 文件添加適用於該文件代碼的特殊規則(second overwrite)。此方式適用於對不同 js 文件設置特定的檢測規則,通常用於在該文件中引入一些全局變量。

下面通過使用 JSLint 並結合不同規則,來對 JSLint 規則的適用範圍和使用方法做一具體介紹。清單 1 是一段基於 dojo 的 JavaScript 代碼。


清單 1. 一段待檢測的 JavaScript 代碼
				 
 dojo.declare("dojox.grid.cells.ComplexHSCombox", [dojox.grid.cells._Widget], { 
	 getWidgetProps: function(inDatum, inRowIndex){ 
 items=[]; 
		 dojo.forEach(this.options[inRowIndex], function(o){ 
 items.push({name: o, value: o}); 
		 }) 
 var store = new dojo.data.ItemFileReadStore({ 
 data: {ide:"name", items: items} 
 }); 
 var grid = this.grid; 
 alert("grid value:" + grid.toString()); 
	 }, 
	 formatNode: function(inNode, inDatum, inRowIndex){ 
 if(!this.widgetClass) return inDatum; 
 if(inDatum == -1 || inRowIndex == -1) return inDatum; 
 this.widget = this.createWidget.apply(this, arguments); 
	 } 
 }); 

接着,使用 JSLint 默認規則對該測試代碼進行檢測,結果圖 2 所示。


圖 2. JSLint 使用默認規則對測試代碼檢測結果
圖 2. JSLint 使用默認規則對測試代碼檢測結果 

結果出現 11 處錯誤。具體分析一下,可以大致歸爲如下幾點:

  1. 未定義變量錯誤

    JSLint 會將未定義變量的操作視爲錯誤,而在該腳本中未定義的變量分爲三種類型,我們需要分別處理:

    1. 第 1、4、7 行錯誤。dojo、dojox 是 dojo 框架的全局變量,不是錯誤,需要我們預定義這些變量屬於合法全局變量,我們將添加規則:/*global dojo: true, dojox: true */ 來解決;
    2. 第 11 行錯誤。alert 是開發過程中用來調試代碼的語句,在 JSLint 中將 console 和 alert、prompt、confirm 視爲 development 方法,需要我們打開 development 模式才允許使用,打開方式是使用規則:devel:true;
    3. 第 3、5、8 行錯誤。這是我們編碼的失誤,我們缺失了私有變量的“var”關鍵字,這會造成全局變量污染,JSLint 發現了這一問題,需要我們及時將“var”添加到變量前。
  2. 缺失分號錯誤

    第 6 行錯誤。JSLint 不允許 JavaScript 句末缺失分號錯誤,因爲這一寫法有時候會降低代碼可讀性,還會造成一些歧義。

  3. 大括號匹配錯誤

    第 14、15 行錯誤。JSLint 不允許控制語句缺失控制語句塊的“{}”,這同樣是比較糟糕的寫法,雖然不會提示錯誤,但仍不是一種提倡的代碼風格,需要我們將這些“{}”補全。

    默認規則有時候並不全面,我們需要定製我們自己的檢測規則來達到規範代碼的目的。鑑於代碼中有可能使用“==”這一不推薦的比較運算符以及不夠緊湊的代碼結構,我們可以添加規則:eqeqeq:true, onevar:true 來重新檢測修改後的代碼,結果如圖 3 所示。



    圖 3. JSLint 使用自定義規則對修改後的測試代碼檢測結果
    圖 3. JSLint 使用自定義規則對修改後的測試代碼檢測結果 

    此時我們只需將“==”改爲“===”,將所有變量定義在一個“var”關鍵字下,檢測就能完全通過。

    此外,還有些常用的 options 這這裏沒有涉及,例如:nomen:是否限制變量以下劃線開頭、plusplus:是否限制“++/--”操作符、white:是否使用嚴格的空格規則、undef:是否限制未定義變量的使用等等。如果想更全面細緻瞭解 JSLint 規則,請查看:JSLint instructions

開始使用 JSLint

最簡單直接的使用 JSLint 的方式是下載 Rhino,以命令行方式直接對特定 JavaScript 腳本進行語法檢查。Rhino 是 Mozilla 提供的純 Java 實現的開源 JavaScript 引擎,可在 java 環境中爲 JavaScript 提供運行環境。讀者可以在 這裏下載 Rhino。

整個過程可以分爲如下幾個步驟:

  1. 確定自定義規則集:這裏只使用一個 var 定義所有的變量、不允許使用 ++/-- 運算符、不允許使用 == 運算符,形成的 options:{onevar:true, plusplus:true, eqeqeq=true}。
  2. 解壓 rhino 壓縮包,裏面包含了 rhino(js.jar) 和 jslint(jslint.js),我們將 js.jar 添加的操作系統的 classpath 裏,方便隨處執行;並將 jslint.js 和 test.js 放在一起(當然也可以不放在一起,之後執行的時候鍵入不同的路徑即可)。
  3. 添加 options 到 JSLint:我們可以選擇修改 JSLint 源碼或是在待檢測的 JavaScript 文件頭部添加註釋型規則。JSLint.js 源碼片段如圖 2 所示,在 536 行 if 語句之後:!JSLINT(input, {[options]}) 是 JSLint 的執行方法,我們的 options 就放在紅色方框所在位置;如果要在待測文件頭部添加註釋類型規則,更加簡單,將如下格式的註釋添加到待檢測 JavaScript 腳本文件頂部即可:
 /*jslint onevar:true, plusplus:true, eqeqeq=true */ 


圖 4. JSLint.js 的部分代碼片段
圖 4. JSLint.js 的部分代碼片段 

需要指出的是,rhino 包裏面提供的 jslint.js 並非原始的 jslint,而是修改過的壓縮版本,它在文件最後添加了用於修改 options 的 JavaScript 代碼,這也正是我們添加自定義規則的切入點。

> 打開 DOS 命令行,鍵入如下命令(將 JSLlint.js 和 test.js 放到當前命令行路徑下),可以看到如圖 5 所示的檢測結果。

 >java org.mozilla.javascript.tools.shell.Main jslint.js test.js 


圖 5. JSLint 在 rhino 引擎下驗證 JavaScript 代碼的結果
圖 5. JSLint 在 rhino 引擎下驗證 JavaScript 代碼的結果 

清單 2 是 test.js 文件的內容,對結果進行簡單分析,我們可以發現 source 文件存在如下問題:

  • 由於 “onevar”規則的限制,檢測出第三行和第四行過多 var 的錯誤;
  • 由於“++/--”規則的限制,檢測出第五行使用“--”的錯誤;
  • 由於“==”規則的限制,檢測出第六行使用“==”的錯誤;
  • 由於 jslint 不能允許無分號結尾的問題,檢測出第十二行缺失句末分號的錯誤。

清單 2. 待測試代碼
				 
 Array.prototype.indexOf = function(obj){ 
	 var result = -1; 
	 var length = this.length; 
	 var i=length - 1; 
 for(i; i>=0 ; i--) { 
		 if (this[i] == obj) { 
			 result = i; 
			 break; 
		 } 
	 } 
	 return result; 
 } 

至此,我們已經可以通過自定義規則的方式來驅動 JSLint 對特定 JavaScript 腳本進行規範化的檢查,但這只是第一步,要在實際開發環境中真正使用 JSLint 保證代碼質量,我們還需要在持續集成和開發環境中整合 JSLint。

在 Ant 中整合 JSLint

Ant 是 Java 開發中常見構建工具之一,通過 Ant 進行項目的持續構建,能及時發現項目代碼中存在問題,保證交付質量。服務器端 Java 代碼在持續構建過程中的代碼檢測工具已經十分成熟,例如 findbugs,而客戶端代碼的檢測工作往往被忽略,下面我們將詳細介紹如何在 Ant 中配置一項 JSLint 檢測任務來完成客戶端代碼的檢測任務。

jslint4java 是一款用來驅動 JSLint 在 rhino 引擎下檢測 JavaScript 代碼的開源 Java 工具,它相當於一個檢測過程的容器。而 jslint4java_ant 則負責將 jslint4java 作爲 Ant 任務來調用,能夠支持用戶自定義 options 以及多文件同時檢測的功能。

這裏將首先創建一個示例項目作爲 Ant 測試環境,項目結構如圖 6 所示,首先將 jslint4java、jslint4java-ant、rhino 三個包添加到類路徑,建立待檢測的 JS 文件夾:JSFiles,並且把需要檢測 JS 文件添加到該文件夾當中,添加 build.xml(如清單 3 所示)並添加 JSLint 任務,這裏我們忽略常見的 Java 編譯打包任務。


圖 6. JSLintTask 項目結構
圖 6. JSLintTask 項目結構 

清單 3. build.xml 內容
				 
 <?xml version="1.0" encoding="UTF-8"?> 
 <project name="Test" xmlns:jsl="antlib:com.googlecode.jslint4java"> 
	 <path id="ant.tasks.classpath"> 
	    <fileset dir="lib" includes="*.jar" /> 
	 </path> 
 <taskdef uri="antlib:com.googlecode.jslint4java" classpathref="ant.tasks.classpath"
 resource="com/googlecode/jslint4java/antlib.xml"/> 
	 <target name="jslint"> 
 <! — - jslint 任務的配置如下 ,options 是用戶自定義規則 - — > 
		 <jsl:jslint options="undef=true, white=false"> 
 <!- — predef 是預定義全局變量的地方 —– > 
			 <predef>dojo, dojox, dijit</predef> 
 <!- — formatter plain, 沒有 destfile 則表示輸出到控制檯 - – > 
			 <formatter type="plain" /> 
 <!- — formatter report 表示輸出成 html 格式的 report - – > 
			 <formatter type="report" destfile="report.html" /> 
 <!- — 通過指定 fileset 設置要進行檢測的 JavaScript 文件集 --> 
			 <fileset dir="JSFiles" includes="*.js" excludes="*.pack.js" /> 
		 </jsl:jslint> 
	 </target> 
 </project> 

<jsl:jslint> 元素的 Options 屬性接受自定義的規則。前文所介紹的各種規則(除了全局變量預定義)都可以在此配置,格式與修改源文件方式以及文件頂部註釋方式相同。

<predef> 元素用來預定義合法的全局變量。例如我們在項目中使用了 dojo 框架,那麼這裏我們可以寫上:<predef>dojo, dojox, dijit</predef>,而如果使用 jQuery,那麼情況可能會複雜一點,因爲要預定義 $ 符號,爲了防止 Ant 將 $ 作爲屬性標記來處理需要多加一個 $ 符:<predef>jQuery, $$</predef>。

<formatter> 元素是用來定義 JSLint 檢測結果的輸出方式。jslint4java 支持五種輸出方式:

  • console 方式,即 type=”plain”,缺失 destfile 屬性;
  • plain text 方式,即 type=”plain”,destfile 指定目標輸出文件;
  • xml 方式,即 type=”xml”,destfile 指定目標輸出文件;
  • junit 方式,即 type=”junit”,destfile 指定目標輸出文件;
  • report 方式,即 type=”report”,destfile 指定目標輸出文件,格式爲 html。

<fileset> 元素用來指定 JSLint 要檢測的目錄和文件,dir 屬性指定需要檢測的目錄,includes 屬性則通過通配符來指定在目標文件夾中檢測符合該通配符的文件,excludes 則恰恰相干,用來過濾一些文件,使他們免於檢測,比如一些已經壓縮的 JavaScript 文件。

經過以上配置,當構建項目時,我們可以得到的需要的 HTML 格式的檢測結果,具體內容如圖 7 所示。


圖 7. report.html 裏對單個文件的分析結果
圖 7. report.html 裏對單個文件的分析結果 

從輸出的內容不難發現,JSLint 已經將項目中 JSFiles 文件夾下的四個 JavaScript 文件都進行了逐一的檢測,並且將錯誤信息(包括錯誤類型,錯誤位置)清晰的輸出出來,方便我們查看錯誤點。不僅如此,輸出還包含了該文件代碼中的全局變量、成員方法、成員變量,這些內容能夠幫助我們瞭解該文件 JavaScript 代碼的主要內容。

在 Eclipse 中使用 JSLint

除了在項目構建過程中對代碼質量進行檢測外,開發人員在開發過程中保持良好的編碼風格也是保證產品最終質量的有效途徑之一。作爲常用的集成開發環境之一,Eclipse 提供了豐富的插件來輔助開發。RockStart 爲 Eclipse 提供了 JSLint 插件,以便幫助開發人員在開發過程中實時對所寫代碼進行檢查。

該插件的使用方式十分簡單,安裝完後,任意在文件內容、文件列表、文件夾上右鍵單擊,都可以出現“Rockstarapps”選項,點擊“validate with JSlint”選項即可對目標文件或是文件夾進行 JSLint 的規則檢測,並且在檢測前,系統會彈出一個規則選取的對話框如圖 8 所示,讓用戶自由配置所需要的規則。設置完畢,點擊完成,開發人員便會看到圖 9 所示視圖,所有當前檢測的問題都會在“問題”視圖中羅列。


圖 8. 規則配置對話框
圖 8. 規則配置對話框 

圖 9. 檢測結果視圖
圖 9. 檢測結果視圖 

需要注意的一點是,開發人員通常需要配置比持續構建環境中 Ant JSLint 任務所使用更爲嚴格的規則集,並在代碼提交之前修改掉所有出現的問題提示,以保證在提交代碼後順利通過項目的構建任務。

結束語

本文主要向讀者詳細介紹了 JSLint 這一 JavaScript 代碼質量工具的作用、原理以及基本使用方式,並通過與 Ant、Eclipse 的整合向讀者描述了一個全面的使用場景。JSLint 是一款優秀和實用的工具,能夠在產品的持續開發過程幫助保證代碼質量,降低風險,它的應用遠不止以上的幾種方式,您可以靈活的配置它來實現您自己的代碼檢測任務。


參考資料

學習


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章