前端javascript代码编写中,有一个不错的工具叫JSLint,能够检查代码规范化,压缩JS,CSS等,可是他的语法规范检查我的以为太“苛刻”了,会提示各类各样的问题修改建议,有时候提示的信息咱们看的莫名其妙,这里,先转载一下携程UED的一个技术文章(原文连接ued.ctrip.com/blog/?p=273…),看看JSLint的错误提示都是什么意思:javascript
一直觉得检查JS语法错误非jslint不可,不过使用起来老是以为过重量级了一点点。php
后来无心中发现了一个叫jshint的东东。html
首先介绍一下,jshint和jslint的差异在哪里。前端
摘自官网的一段内容java
JSHint is a fork of Douglas Crockford’s JSLint that is designed to be more flexible than the original. Our goal is to make a tool that helps you to find errors in your JavaScript code and to enforce your favorite coding style.node
We realize that people use different styles and conventions, and we want our tool to adjust to them. JSHint will never enforce one particular convention.jquery
大概的意思就是,JSHint比起JSLint而言,会更加轻量级一些。它可以找出代码中的语法错误,而且建议更好的一种编码风格。固然,它也不是强制性的非要你根据它规定的编码风格来作。由于它提供了一系列的配置,你能够随时关掉某些你以为没必要要的错误提示。这个我后面会介绍到。ajax
那么如何使用jsHint检查错误呢?用起来很是简单哦~正则表达式
var result = JSHINT(source, options);复制代码
先解释一下参数和返回值:express
第一个参数source : 必选项。表示须要检查的代码,js或者json,能够传一个字符串或者一个数组。若是传字符串,须要用’\r’或者’\n’来分隔一行一行的代码;若是传数组,则每个数组元素表示一行的代码。
第二个参数option : 可选项。表示代码检查的配置项。大部分的都是bool类型的,也有一部分,例如predef,能够是一个array,含有全局变量或者全局方法;或者是一个object,key是全局变量或者方法,value是一个bool值,表示是否被定义过。
返回值:若是代码没有问题,JSHINT会返回一个true;不然返回false。
详细的说明
1. 关于第一个参数
因为只能传入一个字符串或者数组。可是若是须要根据一个js的连接来检查此文件呢?
我尝试使用ajax方法获取js的内容(虽然是能够跨域的),可是若是页面是gb2312的取回来就会乱码,那么用jshint页面检查的话,读到中文部分就会报错”unsafe charater”。尝试使用二进制的responseBody进行转码,可是没有找到是适合的js转码方法。
后来,我写了一个php的中转页面,用file_get_contents的方法读取文件,使用mb_detect_encoding检测页面编码
<?php
mb_detect_order("GB2312,GBK,UTF-8,ASCII");
$url = $_REQUEST["url"];
$str = file_get_contents($url); if(!isset($url) || !$str){
echo "";
}else{
$getcontent = iconv(mb_detect_encoding($str), "utf-8", $str);
echo $getcontent;
}
?>复制代码
固然,也有同窗提到能够将js文件获取下来,存到本地。读取的时候判断编码来读取,也是一种方法。不过我没有尝试过。
2. 关于第二个参数
我以为这个才是JSHINT的精髓,由于每个配置项均可以定义你要check的深度和广度。
下面就把这一系列的配置项列出来(偶本身翻译的)。如下的这些是官网上面的option。
prop | description |
asi | 是否使用自动插入分号 |
bitwise | 若是是true,则禁止使用位运算符 |
boss | 若是是true,则容许在if/for/while的条件中使用=作赋值操做 |
curly | 若是是true,则要求在if/while的模块时使用TAB结构 |
debug | 若是是true,则容许使用debugger的语句 |
eqeqeq | 若是是true,则要求在全部的比较时使用===和!== |
eqnull | 若是是true,则容许使用== null |
evil | 若是是true,则容许使用eval方法 |
forin | 若是是true,则不容许for in在没有hasOwnProperty时使用 |
immed | 若是是true,则要求“当即调用”(immediate invocations)必须使用括号包起来 |
laxbreak | 若是是true,则不检查换行,那么自动插入分号的选项必须开启。 |
maxerr | 默认是50。 表示多少错误时,jshint中止分析代码 |
newcap | 若是是true,则构造函数必须大写 |
noarg | 若是是true,则不容许使用arguments.caller和arguments.callee |
noempty | 若是是true,则不容许使用空函数 |
nonew | 若是是true,则不容许不作赋值的构造函数,例如new UIWindow(); |
nomen | 若是是true,则不容许在名称首部和尾部加下划线 |
onevar | 若是是true,则在一个函数中只能出现一次var |
passfail | 若是是true,则在遇到第一个错误的时候就终止 |
plusplus | 若是是true,则不容许使用++或者- -的操做 |
regexp | 若是是true,则正则中不容许使用.或者[^…] |
undef | 若是是ture,则全部的局部变量必须先声明以后才能使用 |
sub | 若是是true,则容许使用各类写法获取属性(通常使用.来获取一个对象的属性值) |
strict | 若是是true,则须要使用strict的用法, 详见ejohn.org/blog/ecmasc… |
white | 若是是true,则须要严格使用空格用法。 |
固然,千万别觉得JSHINT就只有这些配置项,在我使用的过程当中,发现不少配置项就须要去读它的源代码才能发现。
好比,当我发现他会报错个人某个自定义函数$animate没有定义的时候,我尝试用/*global $animate*/来声明,可是没有效果。
因而我跟踪代码,发现了以下的代码
function assume() {
if (option.couch)
combine(predefined, couch);
if (option.rhino)
combine(predefined, rhino);
if (option.prototypejs)
combine(predefined, prototypejs);
if (option.node)
combine(predefined, node);
if (option.devel)
combine(predefined, devel);
if (option.dojo)
combine(predefined, dojo);
if (option.browser)
combine(predefined, browser);
if (option.jquery)
combine(predefined, jquery);
if (option.mootools)
combine(predefined, mootools);
if (option.wsh)
combine(predefined, wsh);
if (option.globalstrict && option.strict !== false)
option.strict = true;
}复制代码
当时我在这个后面加了一段代码,准备扩展一个predef的项。
后来才发现,原来JSHINT自己就有一个predef的配置,就像上面说的一个数组或一个object便可。
if(option.predef){
for(var i=0,l=option.predef.length; i<l; i++){
predefined[option.predef[i]] = true;
}
}复制代码
固然,细心的同窗必定发现了,里面也能够声明代码运行的环境。例如jquery/dojo等。
又好比JSHINT检查的时候,针对某些字符会报”unsafe character”的错误,可是若是有些字符恰巧就是咱们须要的怎么办呢?
跟踪代码,发现检查unsafe的正则以下:
cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/复制代码
咱们能够自定义一下cx
3. 返回值
当返回false的时候,咱们如何知道代码哪里出问题了呢?
有如下几个方法:
1. JSHINT.errors
JSHINT.errors是一个object,有如下值:
{
line: 错误的行数,
charater: 错误的字符数,
reason: 问题详细描述信息,
evidence: 出错的代码,
raw: 本来的描述信息,
a: the first detail,
b: the second detail,
c: the third detail,
d: the fourth detail
}复制代码
2. JSHINT.report(limited)
参数limited若是为true,则表示report仅仅输出错误(errors)
返回一个相似report的最终结果。能够被放置在html中。
3. JSHINT.data()
返回一个object格式的数据结果, 有如下值:
{
errors:[
{
line: 错误的行数[number],
charater: 错误的字符数[number],
reason: 问题详细描述信息[string],
evidence: 出错的代码[string]
}
],
functions: [
name: 函数名称[STRING],
line: 错误的行数[NUMBER],
last: NUMBER,
param: [
参数[STRING]
],
closure: [
闭包[STRING]
],
var: [
STRING
],
exception: [
STRING
],
outer: [
STRING
],
unused: [
STRING
],
global: [
STRING
],
label: [
STRING
]
],
globals: [
STRING
],
member: {
STRING: NUMBER
},
unuseds: [
{
name: STRING,
line: NUMBER
}
],
implieds: [
{
name: STRING,
line: NUMBER
}
],
urls: [
STRING
],
json: 是不是json的数据[BOOLEAN]复制代码
使用下来,jshint对代码的检查很是不错的。
可是以为jshint有些地方能够改进,例如全部的报错信息都是分散在四面八方。这里一句warning(“xx”),那里一句warning(“yy”)。不像wikipedia有一个统一管理message的地方,并且有语言版本的选择。
无奈之下,我只能增长了一下message的翻译,而且小改了一下warning的函数
globalMsg = {
“Missing semicolon.” : “缺乏分号.”,
“Use the function form of \”use strict\”.” : “使用标准化定义function.”,
“Unexpected space after ‘-’.” : “在’-'后面不该出现空格.”, “Expected a JSON value.” : “请传入一个json的值.”, “Mixed spaces and tabs.”: “空格和TAB重复.”, “Unsafe character.” : “不安全的字符.”, “Line too long.”: “本行中的字符超过设定的最大长度.”, “Trailing whitespace.”: “本行末尾有过多无用空格.”, “Script URL.” : “脚本URL.”, “Unexpected {a} in ‘{b}’.” : “在 ‘{b}’ 中不应出现 {a}.”, “Unexpected ‘{a}’.” : “不应在此出现’{a}’.”, “Strings must use doublequote.” : “字符串须要用双引号”, “Unnecessary escapement.” : “不须要转义”, “Control character in string: {a}.” : “在字符串中出现了Control的字符”, “Avoid \\’.” : “避免 \\”, “Avoid \\v.” : “避免 \\v”, “Avoid \\x-.” : “避免 \\x-”, “Bad escapement.” : “错误的转义字符”, “Bad number ‘{a}’.” : “错误的数字 ‘{a}’”, “Missing space after ‘{a}’.” : “在’{a}’以后缺乏空格”, “Don’t use extra leading zeros ‘{a}’.” : “不要再’{a}’的前面用多余的0″, “Avoid 0x-. ‘{a}’.” : “避免使用 0x-. ‘{a}’.”, “A trailing decimal point can be confused with a dot ‘{a}’.” : “在’{a}’中使用点尾随小数点”, “Unexpected comment.” : “不应在此处出现注释”, “Unescaped ‘{a}’.” : “没有转义 ‘{a}’”, “Unexpected control character in regular expression.” : “在正则表达式中出现了control字符”, “Unexpected escaped character ‘{a}’ in regular expression.” : “在正则表达式中出现了没有转义的字符 ‘{a}’”, “Expected ‘{a}’ and instead saw ‘{b}’.” : “应该用 ‘{a}’代替’{b}’”, “Spaces are hard to count. Use {{a}}.” : “空格难以统计,请使用 {{a}}”, “Insecure ‘{a}’.” : “不安全的 ‘{a}’”, “Empty class.” : “空的class”, “Expected a number and instead saw ‘{a}’.”:“应该用数字代替’{a}’”, “‘{a}’ should not be greater than ‘{b}’.”:“‘{a}’不该该比’{b}’大”, “‘hasOwnProperty’ is a really bad name.”: “‘hasOwnProperty’是关键字”, “‘{a}’ was used before it was defined.”:“‘{a}’未定义就已经使用了.”, “‘{a}’ is already defined.”:“‘{a}’被重复定义”, “A dot following a number can be confused with a decimal point.”:“数字后面的一个点会被误认为是十进制的小数点”, “Confusing minusses” : “容易混淆的负数表达-”, “Confusing plusses.” : “容易混淆的正数表达+”, “Unmatched ‘{a}’.” : “没法匹配的’{a}’”, “Expected ‘{a}’ to match ‘{b}’ from line {c} and instead saw ‘{d}’.”:“在行{c}中须要用’{a}’和’{b}’匹配,用来代替’{d}’”, “Unexpected early end of program.”:“程序不可预期的提早终止”, “A leading decimal point can be confused with a dot: ‘.{a}’.”:“‘{a}’前的点容易混淆成小数点”, “Use the array literal notation [].”:“使用数组的符号 []“, “Expected an operator and instead saw ‘{a}’.”:“须要用一个符号来代替’{a}’”, “Unexpected space after ‘{a}’.”:“在’{a}’以后不能出现空格”, “Unexpected space before ‘{a}’.”:“在’{a}’以前不能出现空格”, “Bad line breaking before ‘{a}’.”:“在’{a}’以前错误的换行”, “Expected ‘{a}’ to have an indentation at {b} instead at {c}.”:“‘{a}’须要在{c}而不是{b}处缩进”, “Line breaking error ‘{a}’.”:“换行错误 ‘{a}’”, “Unexpected use of ‘{a}’.”:“此处不能用’{a}’”, “Bad operand.”:“错误的操做数”, “Use the isNaN function to compare with NaN.”:“使用isNaN来与NaN比较”, “Confusing use of ‘{a}’.”:“容易混淆的’{a}’的使用”, “Read only.”:“只读的属性”, “‘{a}’ is a function.”:“‘{a}’是一个函数”, ‘Bad assignment.’:“错误的赋值”, “Do not assign to the exception parameter.”:“不要给额外的参数赋值”, “Expected an identifier in an assignment and instead saw a function invocation.”:“在赋值的语句中须要有一个标识符,而不是一个方法的调用”, “Expected an identifier and instead saw ‘{a}’ (a reserved word).”:“须要有一个标识符,而不是’{a}’(保留字符)”, “Missing name in function declaration.”:“在方法声明中缺乏名称”, “Expected an identifier and instead saw ‘{a}’.”:“须要有一个标识符,而不是’{a}’”, “Inner functions should be listed at the top of the outer function.”:“内部函数的声明应该放在此函数的顶部。”, “Unreachable ‘{a}’ after ‘{b}’.”:“在’{b}’以后没法获取’{a}’”, “Unnecessary semicolon.”:“没必要要的分号”, “Label ‘{a}’ on {b} statement.”:“将’{a}’放在{b}的声明中”, “Label ‘{a}’ looks like a javascript url.”:“‘{a}’看上去像一个js的连接”, “Expected an assignment or function call and instead saw an expression”:“须要一个赋值或者一个函数调用,而不是一个表达式.”, “Do not use ‘new’ for side effects.”:“不要用’new’语句.”, “Unnecessary \”use strict\”.”:“没必要要的\”use strict\”.”, “Missing \”use strict\” statement.”:“缺乏\”use strict\”的声明”, “Empty block.”:“空的模块”, “Unexpected /*member ‘{a}’.”:“不该出现 /*元素 ‘{a}’.”, “‘{a}’ is a statement label.”:“‘{a}’是一个声明”, “‘{a}’ used out of scope.”:“‘{a}’使用超出范围”, “‘{a}’ is not allowed.”:“不容许使用’{a}’”, “‘{a}’ is not defined.”:“‘{a}’没有被定义”, “Use ‘{a}’ to compare with ‘{b}’.”:“使用’{a}’与’{b}’相比”, “Variables should not be deleted.”:“变量须要被删除”, “Use the object literal notation {}.”:“使用对象的文字符号 {}”, “Do not use {a} as a constructor.”:“不要使用{a}做为一个构造对象”, “The Function constructor is eval.”:“The Function constructor is eval.”, “A constructor name should start with an uppercase letter.”:“一个构造对象的名称必须用大写字母开头.”, “Bad constructor.”:“错误的构造对象”, “Weird construction. Delete ‘new’.”:“构造对象有误,请删除’new’”, “Missing ‘()’ invoking a constructor.”:“缺乏括号()”, “Avoid arguments.{a}.”:“避免参数.{a}.”, “document.write can be a form of eval.”:“document.write是eval的一种形式”, ‘eval is evil.’:“尽可能不要使用eval”, “Math is not a function.”:“Math不是一个函数”, “Missing ‘new’ prefix when invoking a constructor.”:“此处缺乏了’new’”, “Missing radix parameter.”:“缺乏参数”, “Implied eval is evil. Pass a function instead of a string.”:“传递一个函数,而不是一个字符串”, “Bad invocation.”:“错误的调用”, “['{a}'] is better written in dot notation.”:“['{a}']最好用点.的方式”, “Extra comma.”:“多余的逗号”, “Don’t make functions within a loop.”:“不要用循环的方式建立函数”, “Unexpected parameter ‘{a}’ in get {b} function.”:“在{b}方法中不应用到参数’{a}’”, “Duplicate member ‘{a}’.”:“重复的’{a}’”, “Expected to see a statement and instead saw a block.”:“此处应该是语句声明.”, “Too many var statements.”:“过多var的声明”, “Redefinition of ‘{a}’.”:“‘{a}’被重复定义”, “It is not necessary to initialize ‘{a}’ to ‘undefined’.”:“无需将’{a}’初始化为’undefined’”, “Expected a conditional expression and instead saw an assignment.”:“此处须要一个表达式,而不是赋值语句”, “Expected a ‘break’ statement before ‘case’.”:“在’case’以前须要有’break’.”, “Expected a ‘break’ statement before ‘default’.”:“在’default’以前须要有’break’.”, “This ‘switch’ should be an ‘if’.”:“此处’switch’应该是’if’.”, “All ‘debugger’ statements should be removed.”:“请删除’debugger’的语句”, “‘{a}’ is not a statement label.”:“‘{a}’不是一个声明标签.”, “Expected an assignment or function call and instead saw an expression.”:“须要一个语句或者一个函数调用,而不是一个表达式”, “Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.”:“函数的声明不能放在相似if的块中,须要放在外部函数的顶部.” },复制代码
warning函数增长了一个m = globalMsg[m] || m;
function warning(m, t, a, b, c, d) {
var ch, l, w;
t = t || nexttoken;
if (t.id === ‘(end)’) { // `~
t = token;
}
l = t.line || 0;
ch = t.from || 0;
m = globalMsg[m] || m;
w = {
id: ‘(error)’,
raw: m,
evidence: lines[l - 1] || ”,
line: l,
character: ch,
a: a,
b: b,
c: c,
d: d
};
w.reason = m.supplant(w);
JSHINT.errors.push(w);
if (option.passfail) {
quit(‘Stopping. ‘, l, ch);
}
warnings += 1;
if (warnings >= option.maxerr) {
quit(“Too many errors.”, l, ch);
}
return w;
}复制代码
若是你须要一个轻量级的语法检查工具,那么jshint仍是一个蛮驾轻就熟的工具。若是可以更深刻的读一下JSHINT的代码,收获应该不小。
针对本身项目中遇到的一些提示,作一些举例说明:
1 [W099]:Mixed spaces and tabs
这个错误是最多见的,意思是在同一行中,空格和Tab缩进混合使用了,修改很简单,通常是删除Tab缩进,所有改成空格。为了方便,咱们能够把编辑器的Tab缩进设置成2个或4个空格,来代替原有的缩进。
2 [W030]:Expected an assignment or function call and instead saw an expression
这个错误提示的很诡异,我是用以下代码提示的这个错误 index-1 <0 ? index = 0:index = index - 1; 这是一个逗号表达式,可是JSLInt认为这里不该该用表达式,而必须是一个函数,因此,若是很是在意这个错误,就改成if else 语句吧
3 [W041]:Use '===' to compare with ...
这个错误是说,咱们要是用全等来代替等于,若是表达式两边的数据类型是一致的话,建议使用全等来判断
4 [W033]:Missing semicolon
缺乏分号;这个通常都是本身忘记写了吧,可是有一个须要注意的是,对于只有一句的结构,后面也须要写分号。例如:if(index<0) {index=tcount-1} 这句代码,正确写法是if(index<0) {index=tcount-1;},我是常常忘记这里写分号,汗...
其余还有一些错误提示就对照一下改吧,要培养本身良好的代码风格和书写习惯。