因而,我们需要一个有效的 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 工作过程示意图
如图所示,规则集的配置方式有三种:
- 直接通过修改 JSLint.js 源码来修改默认规则。
- 在 JSLint 函数运行时,同时设置 options 参数,动态改变其规则选项(first overwrite)。此方式适用于对批量 js 文件使用同样的一组自定义规则。
- 通过在待检测的 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 使用默认规则对测试代码检测结果
结果出现 11 处错误。具体分析一下,可以大致归为如下几点:
- 未定义变量错误
JSLint 会将未定义变量的操作视为错误,而在该脚本中未定义的变量分为三种类型,我们需要分别处理:
- 第 1、4、7 行错误。dojo、dojox 是 dojo 框架的全局变量,不是错误,需要我们预定义这些变量属于合法全局变量,我们将添加规则:/*global dojo: true, dojox: true */ 来解决;
- 第 11 行错误。alert 是开发过程中用来调试代码的语句,在 JSLint 中将 console 和 alert、prompt、confirm 视为 development 方法,需要我们打开 development 模式才允许使用,打开方式是使用规则:devel:true;
- 第 3、5、8 行错误。这是我们编码的失误,我们缺失了私有变量的“var”关键字,这会造成全局变量污染,JSLint 发现了这一问题,需要我们及时将“var”添加到变量前。
- 缺失分号错误
第 6 行错误。JSLint 不允许 JavaScript 句末缺失分号错误,因为这一写法有时候会降低代码可读性,还会造成一些歧义。
- 大括号匹配错误
第 14、15 行错误。JSLint 不允许控制语句缺失控制语句块的“{}”,这同样是比较糟糕的写法,虽然不会提示错误,但仍不是一种提倡的代码风格,需要我们将这些“{}”补全。
默认规则有时候并不全面,我们需要定制我们自己的检测规则来达到规范代码的目的。鉴于代码中有可能使用“==”这一不推荐的比较运算符以及不够紧凑的代码结构,我们可以添加规则:eqeqeq:true, onevar:true 来重新检测修改后的代码,结果如图 3 所示。
图 3. JSLint 使用自定义规则对修改后的测试代码检测结果
此时我们只需将“==”改为“===”,将所有变量定义在一个“var”关键字下,检测就能完全通过。
此外,还有些常用的 options 这这里没有涉及,例如:nomen:是否限制变量以下划线开头、plusplus:是否限制“++/--”操作符、white:是否使用严格的空格规则、undef:是否限制未定义变量的使用等等。如果想更全面细致了解 JSLint 规则,请查看:JSLint instructions。
回页首
开始使用 JSLint
最简单直接的使用 JSLint 的方式是下载 Rhino,以命令行方式直接对特定 JavaScript 脚本进行语法检查。Rhino 是 Mozilla 提供的纯 Java 实现的开源 JavaScript 引擎,可在 java 环境中为 JavaScript 提供运行环境。读者可以在 这里下载 Rhino。
整个过程可以分为如下几个步骤:
- 确定自定义规则集:这里只使用一个 var 定义所有的变量、不允许使用 ++/-- 运算符、不允许使用 == 运算符,形成的 options:{onevar:true, plusplus:true, eqeqeq=true}。
- 解压 rhino 压缩包,里面包含了 rhino(js.jar) 和 jslint(jslint.js),我们将 js.jar 添加的操作系统的 classpath 里,方便随处执行;并将 jslint.js 和 test.js 放在一起(当然也可以不放在一起,之后执行的时候键入不同的路径即可)。
- 添加 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 的部分代码片段
需要指出的是,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 代码的结果
清单 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 项目结构
清单 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 里对单个文件的分析结果
从输出的内容不难发现,JSLint 已经将项目中 JSFiles 文件夹下的四个 JavaScript 文件都进行了逐一的检测,并且将错误信息(包括错误类型,错误位置)清晰的输出出来,方便我们查看错误点。不仅如此,输出还包含了该文件代码中的全局变量、成员方法、成员变量,这些内容能够帮助我们了解该文件 JavaScript 代码的主要内容。
在 Eclipse 中使用 JSLint
除了在项目构建过程中对代码质量进行检测外,开发人员在开发过程中保持良好的编码风格也是保证产品最终质量的有效途径之一。作为常用的集成开发环境之一,Eclipse 提供了丰富的插件来辅助开发。RockStart 为 Eclipse 提供了 JSLint 插件,以便帮助开发人员在开发过程中实时对所写代码进行检查。
该插件的使用方式十分简单,安装完后,任意在文件内容、文件列表、文件夹上右键单击,都可以出现“Rockstarapps”选项,点击“validate with JSlint”选项即可对目标文件或是文件夹进行 JSLint 的规则检测,并且在检测前,系统会弹出一个规则选取的对话框如图 8 所示,让用户自由配置所需要的规则。设置完毕,点击完成,开发人员便会看到图 9 所示视图,所有当前检测的问题都会在“问题”视图中罗列。
图 8. 规则配置对话框
图 9. 检测结果视图
需要注意的一点是,开发人员通常需要配置比持续构建环境中 Ant JSLint 任务所使用更为严格的规则集,并在代码提交之前修改掉所有出现的问题提示,以保证在提交代码后顺利通过项目的构建任务。
回页首
结束语
本文主要向读者详细介绍了 JSLint 这一 JavaScript 代码质量工具的作用、原理以及基本使用方式,并通过与 Ant、Eclipse 的整合向读者描述了一个全面的使用场景。JSLint 是一款优秀和实用的工具,能够在产品的持续开发过程帮助保证代码质量,降低风险,它的应用远不止以上的几种方式,您可以灵活的配置它来实现您自己的代码检测任务。