靜態代碼檢查是開發工作中不可缺少的一環,畢竟對於程序化的工作人的眼睛是不可靠的,更何況是自己的眼睛看自己的代碼。即使最后的運行結果通過,但可能存在一些未定義的變量、定義了但最后沒用過的變量、分號有沒有加(看團隊規則)以及其他的問題。給力的工具必不可缺。
在本文要介紹的JSHint之前還有JSLint,道格拉斯的作品,應該是JavaScript精粹的附屬產物。。。要求你必須使用它的規則,對於JavaScript這樣一個靈活的語言,還是要用一個靈活的工具,JSHint就是就是這樣。
安裝
首先需要安裝了Nodejs和npm,然后 npm install -g jshint 就好了。
這樣你就可以使用命令行使用JSHint了。
當然他也提供了巨多的編輯器插件,比如Sublime Text 3。
以及Grunt或者Gulp之類的插件。
配置
安裝完了就可以使用了。
命令行下直接: jshint myfile.js 就ok,命令行里就可以顯示錯誤或者警告。
編輯器下一般直接顯示在出錯或者警告的行數處。比如Sublime3 下,在當前文件下,快捷鍵 Ctrl+Alt+J 就會顯示信息( Ctrl+Alt+C 關閉信息顯示):
更多快捷鍵看插件的設置,可以自定義。
不過這些顯示都是JSHint默認的配置,我們可以自定義配置更好的滿足需求。
也很簡單,在項目根目錄下建立一個 .jshintrc 文件,這個文件就是JSHint的配置文件,JSHint會自動識別這個文件,根據這里面的規則對你的文件進行檢查。
(windows下建立前面帶點的文件會不讓建立,一種方法直接在Sublime Text里建立,另一種方法在文件名后加個點即可)
JSHint的配置分為四類:
1、Enforcing:如果這些屬性設置為true,表明這個代碼風格夠嚴格的,比如是否使用嚴格(strict)模式、變量駝峰式命名、是不是for-in循環里必須得有hasOwnProperty等等
2、Relaxing:比如是否使用分號,是否支持下一代ES語法等等。
3、Environments:你的代碼所在的環境(Nodejs、瀏覽器、jQuery。。)
4、自定義的全局屬性,比如我司的NEJ和Regular,這兩個全局變量JSHint是不知道的,放在這里JSHint不會報出錯誤信息。
默認的JSHint的配置看這里:JSHint默認配置(Sublime插件自定義了默認的配置,你可以在插件的配置里看到,甚至可以直接修改它)
對這些配置的講解:配置詳解
都是英文的,不過配合着四六級的水平和有道翻譯,基本木有問題。下面是一個簡單的配置示例。
{ "strict" : false, "undef" : true, "unused" : true, "asi" : true, "evil" : false, "browser": true, "devel": true, "globals" : { "NEJ": true, "Regular": true } }
有時候,我們不希望它檢查一些文件(比如一些庫文件),這時候可以新建一個 .jshintignore 文件,把需要忽略的文件名寫在里面(支持通配符),同樣放到項目根目錄下即可。
build/ src/**/tmp.js
自定義Reporter
JSHint源碼里有一個reporter.js,定義錯誤提示信息改怎樣輸出,同樣可以自定義。默認的是這樣的:
"use strict"; module.exports = { reporter: function (res) { var len = res.length; var str = ""; res.forEach(function (r) { var file = r.file; var err = r.error; str += file + ": line " + err.line + ", col " + err.character + ", " + err.reason + "\n"; }); if (str) { process.stdout.write(str + "\n" + len + " error" + ((len === 1) ? "" : "s") + "\n"); } } };
基本的格式就是:
module.exports = { reporter: function (reporter) { //.... } };
每個reporter都符合一定的格式:
{ file: [string, filename] error: { id: [string, usually '(error)'], code: [string, error/warning code], reason: [string, error/warning message], evidence: [string, a piece of code that generated this error] line: [number] character: [number] scope: [string, message scope; usually '(main)' unless the code was eval'ed] [+ a few other legacy fields that you don't need to worry about.] } }
比如你可以讓他不輸出到控制台而是打印到txt文件里(不知為何異步寫入文件一直不成功,最后只好用同步函數):
'use strict'; var fs = require('fs'); module.exports = { reporter: function (res) { var len = res.length; var str = ''; var filename = ''; res.forEach(function (r, i) { filename = r.file; var err = r.error; if(i === 0) str += filename + '\n'; str += 'line ' + err.line + ', col ' + err.character + ', ' + err.reason + '\n'; }); if (str) { var output = str + '\n\n'; fs.writeFileSync('message.txt', output); } } };
命令行執行:
jshint --reporter=myreporter.js myfile.js
同樣可以更近一步,如果你想一下把所有該檢查的文件全都檢查了,然后將檢查結果保存到txt里:
// code-check.js
var fs = require('fs'); var path = require('path'); var exec = require('child_process').exec; var curPath = path.join(process.cwd(), 'src', 'javascript'); function travelDir(dir, callback) { fs.readdirSync(dir).forEach(function(file) { var pathname = path.join(dir, file); if(fs.statSync(pathname).isDirectory()) { travelDir(pathname, callback); } else { callback(pathname); } }); } fs.writeFileSync('message.txt', ''); travelDir(curPath, function(file) { exec('jshint --reporter=reporter.js ' + file); });
// reporter.js
'use strict'; var fs = require('fs'); module.exports = { reporter: function (res) { var len = res.length; var str = ''; var filename = ''; res.forEach(function (r, i) { filename = r.file; var err = r.error; if(i === 0) str += filename + '\n'; str += 'line ' + err.line + ', col ' + err.character + ', ' + err.reason + '\n'; }); if (str) { var output = str + '\n\n'; fs.appendFileSync('message.txt', output); } } };
只要 node code-check 就可以了。
不過,Sublime插件下不知道如何自定義。
API
JSHint暴漏了一些接口,既可以在瀏覽器也可以在Nodejs中使用。
瀏覽器下首先加載jshint.js文件:
<script src="node_modules/jshint/dist/jshint.js"></script>
首先檢查js語句是否存在錯誤:
var result = JSHINT(source, options, predef)
source是你要檢查的代碼,可以是字符串,也可以是數組,數組的話每一項代表一行代碼。
options也上面說過的配置項,但不包括Globals
predef是上面說過的Globals
當result返回false的時候,代表語句中有錯誤,這時候調用 JSHINT.data() 就會得到錯誤的詳細信息,看下面的示例:
var source = [ 'function() { console.log("a") }', 'x = 3' ] var options = { undef: true } var predef = { x: true } var result = JSHINT(source, options, predef) console.log(JSHINT.data())
瀏覽器的控制台就會輸出錯誤的詳細信息,這樣我們甚至可以做一個錯誤報告出來,就像公司內部的代碼檢查平台 一樣。
不過有個問題,不可能我們把代碼一行一行的敲到參數里,ajax或者服務器端請求都行,看這里。
所以嘛,還是在node環境下使用最方便:
// check.js
var JSHINT = require('jshint').JSHINT, fs = require('fs'), files = []; process.argv.forEach(function(val) { files.push(val) }) console.log('-----------------------------------------') for(var i = 2; i < files.length; i++) { fs.readFile(files[i], function(err, data) { if(err) { console.log('Error: ' + err) return } if(JSHINT(data.toString())) { console.log('File ' + files[i] + ' has no errors!') } else { console.log('Errors in file ' + files[i]); console.log('') var out = JSHINT.data(), errors = out.errors; for(j = 0; j < errors.length; j++) { console.log(errors[j].line + ': ' + errors[j].character + ' -> ' + errors[j].reason + ' -> ' + errors[j].evidence); } console.log('') console.log('Globals: ') for(var j = 0; j < out.globals.length; j++) { console.log(' ' + out.globals[j]); } } console.log('---------------------------------------------') }) }
使用的時候
node check a.js b.js c.js
最后在控制台輸出錯誤。
有時候掌握一個工具工作效率會提高很多倍,學會做一個懶人。