静态代码检查是开发工作中不可缺少的一环,毕竟对于程序化的工作人的眼睛是不可靠的,更何况是自己的眼睛看自己的代码。即使最后的运行结果通过,但可能存在一些未定义的变量、定义了但最后没用过的变量、分号有没有加(看团队规则)以及其他的问题。给力的工具必不可缺。

在本文要介绍的JSHint之前还有JSLint,道格拉斯的作品,应该是JavaScript精粹的附属产物。。。要求你必须使用它的规则,对于JavaScript这样一个灵活的语言,还是要用一个灵活的工具,JSHint就是就是这样。


配置

安装完了就可以使用了。

命令行下直接: jshint myfile.js

jquery废弃代码检查 jquery代码检查工具_自定义

编辑器下一般直接显示在出错或者警告的行数处。比如Sublime3 下,在当前文件下,快捷键 Ctrl+Alt+J 就会显示信息( Ctrl+Alt+C

更多快捷键看插件的设置,可以自定义。

jquery废弃代码检查 jquery代码检查工具_javascript_02

不过这些显示都是JSHint默认的配置,我们可以自定义配置更好的满足需求。

也很简单,在项目根目录下建立一个 .jshintrc

(windows下建立前面带点的文件会不让建立,一种方法直接在Sublime Text里建立,另一种方法在文件名后加个点即可)

JSHint的配置分为四类:

1、Enforcing:如果这些属性设置为true,表明这个代码风格够严格的,比如是否使用严格(strict)模式、变量驼峰式命名、是不是for-in循环里必须得有hasOwnProperty等等

2、Relaxing:比如是否使用分号,是否支持下一代ES语法等等。

3、Environments:你的代码所在的环境(Nodejs、浏览器、jQuery。。)

4、自定义的全局属性,比如我司的NEJ和Regular,这两个全局变量JSHint是不知道的,放在这里JSHint不会报出错误信息。

都是英文的,不过配合着四六级的水平和有道翻译,基本木有问题。下面是一个简单的配置示例。


{
    "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())



浏览器的控制台就会输出错误的详细信息,这样我们甚至可以做一个错误报告出来,就像公司内部的代码检查平台 一样。

jquery废弃代码检查 jquery代码检查工具_自定义_03

不过有个问题,不可能我们把代码一行一行的敲到参数里。

所以嘛,还是在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('---------------------------------------------')
    })
}