随着前端团队的发展和JS相关的工作和代码的增加,Js代码质量和规范上变得势在必行。统一的代码规范以及编程习惯对提高团队代码质量,也能潜移默化的培养程序员优质的代码习惯。
经过一些资料的查阅,暂选定jshint作为本次js的代码质量的检查控件,具体情况如下:
目标:
程序员本地可在编码时或代码提交前进行代码质量检查,并且在代码提交后上线前,可集中的进行集中代码检查,复查程序员代码。
方案:
本地采用:Sublime+SublimeLinter+jshint
集中采用:gulp + jshint
Sublime配置Jshint
1. 首先安装Nodejs,安装可见
2. 进入cmd运行全局安装jshint :npm install jshint -g
3. Sublime中安装sublimeliner和jshint插件
4. 配置jshint (可在项目文件中加入. Jshintrc文件即可,会自动逐级搜索此文件中的验证配置的)
5. Alt+j 即可看到效果
附录:
Jshint配置中文Doc
增强参数(Enforcing Options)
本类参数设为true,JSHint会产生更多告警。
bitwise
禁用位运算符(如^,,&)
位运算符在JS中很少使用,性能也较差,出现&也很可能是想写&&。
camelcase
使用驼峰命名(camelCase)或全大写下划线命名(UPPER_CASE)
这是条最佳实践,关键不在于采用什么样的命名规则(比如纯小写配下划线),而在于要有规则,在代码中看到不同的命名规则会让人头痛不已。
curly
if和while等语句中使用{}来明确代码块
while (day)
shuffle();
sleep();
虽然缩进表示两条语句都在循环中,但事实却是只有一句循环。
eqeqeq
使用===和!==替代==和!===和!=比较时会对前后元素进行自动转义,作为读者,需要动脑筋想这里可能有什么样的转义规则,加重负担;作为作者,其实很可能是不确定这段代码运行时是怎么样的,想要偷懒。
es3
强制使用ECMAScript 3规范
forin
在for in循环中使用Object.prototype.hasOwnProperty()来过滤原型链中的属性
for (key in obj) {
if (obj.hasOwnProperty(key)) {
// We are sure that obj[key] belongs to the object and was not inherieted.
}
}
for in遍历对象属性的时候,包括继承自原型链的属性,hasOwnProperty可以来判断一个属性是否是对象本身的属性而不是继承得来的。
freeze
禁止复写原生对象(如Array, Date)的原型
Array.prototype.count = function (value) { return 4; };
// -> Warning: Extending prototype of native object: Array.
为原生对象添加属性确实看上去很方便,但也带来了潜在的问题,一是如果项目中有多处为同一个对象添加了同样的属性(或函数),则很可能产生冲突;二是如果某段逻辑依赖于对象属性遍历,则可能产生错误。
immed
匿名函数调用必须
(function() {
// body
}());
而不是
(function() {
// body
})();
这是为了表明,表达式的值是函数的结果,而不是函数本身。
indent
代码缩进宽度(空格数)
前面几个项目我比较喜欢4,新项目我又在尝试2。关键不在于是几,而在于大家都要设成一样的。
latedef
变量定义前禁止使用
JS的变量是函数级作用域,而不是通常所见的块级作用域,简单说
function sum(numbers) {
for (var i = 0, n = numbers.length; i < n; i++) {
var sum = sum + numbers[i];
}
return sum;
}
相当于
function sum(numbers) {
var i, n, sum;
for (i = 0, n = numbers.length; i < n; i++)
{
sum = sum + numbers[i];
}
return sum;
}
这个行为叫做变量声明提升,为了不产生混淆,这条规则建议函数都使用第二种写法。
newcap
构造函数名首字母必须大写
这条最佳实践是为了方便区分构造函数和普通函数,这样在直接调用大写字母开头的函数时,使用者就会想想是不是自己写错了。
不通过new而直接调用构造函数,会使得构造函数中的this指向global对象,从而产生错误。
PS. 有些高手可以通过在构造函数中判断this的指向来判断是否重新new自身,从而让构造函数也能直接调用产生新对象。但这有些高深,加重开发人员和使用人员的负担,也不利于统一编码风格。
noarg
禁止使用arguments.caller和arguments.callee
一方面这两个属性不是所有的浏览器都支持,另一方面这两个属性的使用会导致JS引擎很难优化代码,在未来的JS规范中会被去掉,所以不建议使用。
noempty
禁止出现空的代码块
空的代码块并不是有害的,但是出现的话我们需要考虑下为什么。
nonbsp
禁止non-breaking whitespace
这是Mac键盘在某种情况下可以键入的字符,据说会破坏非UTF8编码的页面。
nonew
禁止使用构造器
new MyConstructor();
构造一个对象,却不给它赋值到某个变量,只是利用构造函数中的逻辑。这个行为完全可以用一个普通函数来完成,不应该借助构造器。
plusplus
禁止使用++和–
不是很赞成把这个选项打成true,不过乱用自增/自减确实也会带来阅读上的障碍。
quotemark
统一使用单引号或双引号
这个最佳实践要求代码风格统一,我比较喜欢统一成单引号。
这是为什么规定最佳实践的一个好例子,在写到字符串的时候我们就不用考虑使用单引号好还是用双引号好,就都用单引号,这在一定程度上也减轻了我们的思考负担。
undef
禁止使用不在全局变量列表中的未定义的变量
function test() {
var myVar = Hello, World;
console.log(myvar); // Oops, typoed here. JSHint with undef will complain
}
如果本地作用域里的变量没有使用var来声明,则会被放到全局作用域下面,众所周知,全局变量时罪恶的源泉。
unused
禁止定义变量却不使用
function test(a, b) {
var c, d = 2;
return a + d;
}
test(1, 2);
// Line 1: b was defined but never used.
// Line 2: c was defined but never used.
这种变量通常是写作过程中遗留下来的垃圾,需要及时清理掉。
strict
强制使用ES5的严格模式
Strict Mode是对JS用法的一些限制,过滤掉了容易出错的特性和不容易优化的特性。
通过在函数开头处加入use strict;来触发严格模式,不要在文件头部加入,因为在JS链接的时候很可能就失效了。
trailing
禁止行尾空格
maxparams
函数可以接受的最大参数数量
函数参数数量应该控制在3个以内,超出则可能造成使用困难,比如需要记忆参数顺序,难以设定默认值等。另外,在JS中可以很方便的使用参数对象来封装多个参数。
maxdepth
代码块中可以嵌入{}的最大深度
maxstatement
函数中最大语句数
maxcomplexity
函数的最大圈复杂度
maxlen
一行中最大字符数
这个是为了减轻代码阅读的困难,简单说就是不要折行。
上面四个参数最终都是为了减小代码的复杂程度,简单轻巧的代码片段更容易阅读和维护。
松弛参数(Relaxing Options)
本类参数设为true,JSHint会产生更少告警。
asi
允许省略分号
JavaScript的语法允许自动补全分号,但是这一特性也会造成难以定位的错误,所以建议写代码时不要省略分号。
boss
允许在if,for,while语句中使用赋值
在条件语句中使用赋值经常是笔误if (a = 10) {},但是牛人(boss)可以把这个特性用的很好,我们作为普通人就算了。
debug
允许debugger语句
debugger语句在产品代码中应该去掉。
eqnull
允许==null
==null通常用来比较=== null
=== undefined
esnext
允许ECMAScript 6规约
目前ES6的特性不是所有的浏览器都支持。
evil
允许使用eval
eval有注入攻击的危险,另一方面也不利于JS引擎优化代码,所以尽量不要使用。
expr
允许应该出现赋值或函数调用的地方使用表达式
funcscope
允许在控制体内定义变量而在外部使用
function test() {
if (true) {
var x = 0;
}
x += 1; // Default: x used out of scope.
// No warning when funcscope:true
}
虽然变量声明提升使得上面的代码可以运行通过,但是读者还是会感到头晕。
globalstrict
允许全局严格模式
在strict中解释了,use strict;放在全局域可能造成JS文件链接错误。
iterator
允许iterator
不是所有的浏览器都支持iterator。
lastsemic
允许单行控制块省略分号
var name = (function() { return Anton }());
高手用得到的特性,我们还是坚持加上分号吧。
laxbreak
允许不安全的行中断(与laxcomma配合使用)
laxcomma
允许逗号开头的编码样式
var obj = {
name: Anton
, handle: valueof
, role: SW Engineer
};
loopfunc
允许循环中定义函数
在循环中定义函数经常会导致错误:
var nums = [];
for (var i = 0; i < 10; i++) {
nums[i] = function (j) {
return i + j;
};
}
nums[0](2); // Prints 12 instead of 2
错误的根源在于function(j)中的i是对循环中的i的引用,而不是赋值。所以在最终函数执行时,i的值是10。
修改的方法是使用闭包:
var nums = [];
for (var i = 0; i < 10; i++) {
(function (i) {
nums[i] = function (j) {
return i + j;
};
}(i));
}
maxerr
JSHint中断扫描前允许的最大错误数
因为最终我们需要清零JSHint报错的,所以这个值用在对已有项目的扫描中。
multistr
允许多行字符串
notypeof
允许非法的typeof操作
proto
允许 proto
不是所有的浏览器都支持proto.
smarttabs
允许混合tab和space排版
SmartTabs方法使用tab进行缩进,使用空格进行代码对齐。比较高级的用法,有兴趣的话可以尝试下。
shadow
允许变量shadow
function test() {
var x = 10;
if (true) {
var x = 20;
}
return x;
}
基于函数作用域,多次定义变量和单次定义是没有区别的,但是会造成阅读障碍。
sub
允许person[name]
JSHint推荐使用person.name代替person[name]
supernew
允许new function() {…}和new Object;
validthis
允许严格模式下在非构造函数中使用this
noyield
允许发生器中没有yield语句
Jshint 错误提示
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}之后缺少空格,
Dont 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. : 不该在此处出现注释,
d {a}. : 没有转义 {a},
Unexpected control character in regular ex pression. : 在正则表达式中出现了control字符,
Unexpected escaped character {a} in regular ex pression. : 在正则表达式中出现了没有转义的字符 {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 exp ression:需要一个赋值或者一个函数调用,而不是一个表达式.,
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.:多余的逗号,
Dont 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 ex pression 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 ex pression.:需要一个语句或者一个函数调用,而不是一个表达式,
Function declarations should not be placed in blocks. Use a function ex pression or move the statement to the top of the outer function.:函数的声明不能放在类似if的块中,需要放在外部函数的顶部.