10月阅读周·编写可测试的JavaScript代码:复杂度之JSLint篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读九个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》。
当前阅读周书籍:《编写可测试的JavaScript代码》。
JSLint
JSLint并不直接衡量软件的复杂度,它只是迫使我们了解代码到底在做什么。这将会减少复杂性,并确保我们不会使用任何过于复杂或容易出错的构造。简而言之,这是验证代码合理性的工具。其灵感来自原始C语言版本的“lint”,JSLint用于对糟糕的代码风格、语法和语义进行分析,检测代码的不良部分。
重构糟糕的代码,将它替换成良好的代码,这就是编写可测试JavaScript代码的本质。请参考如下示例:
function sum(a, b) {
return;
a + b;
}
上述例子非常简单。让我们通过JSLint来运行它:
Error:
Problem at line 2 character 5: Missing 'use strict' statement.
return
Problem at line 2 character 11:Expected';' and instead saw'a'.
return
Problem at line 3 character 8: Unreachable 'a' after 'return'.
a+b;
Problem at line 3 character 8: Expected 'a' at column 5, not column 8.
a+b;
Problem at line 3 character 9: Missing space between 'a' and'+'.
a+b:
Problem at line 3 character 10:Missing space between '+' and 'b'.
a+b;
Problem at line 3 character 10: Expected an assignment or
function call and instead saw an expression.
a+b;
仅仅4行代码就产生7个JSLint错误!是什么地方错了吗?除了缺少use strict语句,最大的问题是return后面的回车换行符。由于分号的插入,该函数将返回undefined。JSLint捕获这个错误,并且质疑了该函数内部的其他空格问题。
空格不仅与可读性相关,而且在本例中,它还会导致一个很难追踪的错误。合理地使用空格,就像JSLint要求的那样;无法阅读和理解的代码也是无法测试的。代码可读性是实现可测试代码的最基本的一步。编写的程序是为了让其他程序员进行维护、增强以及测试的,所以代码的可读性至关重要。JSLint为代码可读性(合理性)提供了很好的检测方法。
如下是修改后的代码:
function sum(a, b) {
return a + b;
}
让我们看另一个看似无错的代码片段:
for(var i=0; i< a.length; i+++)
a[i] i*i;
这一次JSLint无法解析整个代码块,只能到此为止:
Error:
Problem at line 1 character 6: Move'var' declarations to the top of the function.
for (var i = 0;i < a.length; i++)
Problem at line 1 character 6: Stopping.(33% scanned).
第一个问题是for循环中的var声明。JavaScript变量要么在全局作用域,要么在函数作用域。在for循环中声明一个变量并不仅仅是为for循环声明变量。该变量在包含for循环的整个函数内都是可用的。通过该构造函数,创建了一个名为i的变量,并且在整个函数内都是可用的。这就是JSLint要告诉我们将var声明挪到函数顶部的原因。这样编写代码,会令自己困惑不已,更糟糕的是,下一个维护这段代码的程序员也会感到困惑。
虽然这不是很关键,但我们应该摆脱随意声明变量的习惯,而要在函数开始的地方进行声明变量。JavaScript变量是基于函数作用域的,所以在这一点上,理解JavaScript和其他语言之间的区别之后,我们会感到很骄傲!将变量声明挪到函数内部的顶部才是有意义的。
将变量声明移动到函数顶部以后,JSLint又产生了如下几点提示:
Error:
Problem at line 2 character 28: Unexpected '++'.
for(i = 0;i <a.length; i++)
Problem at line 3 character 5: Expected exactly one space between ')' and 'a'.
a[i] i*i;
Problem at line 3 character 5: Expected'{' and instead saw 'a'.
a[i] i*i;
Problem at line 3 character 13:Missing space between 'i' and '*'.
a[i] i*i;
Problem at line 3 character 14:Missing space between '*' and 'i'.
a[i] i*i;
JSLint不提倡使用++运算符。前缀形式和后缀形式的++和--很容易混淆。这可能是代码的最小编写形式,并且是一种非常常见的编程风格,所以在这里JSLint或许有点苛刻,但这些运算符是C/C++遗留下来的指针运算符,在JavaScript里没必要使用,所以要特别注意。一般情况下,如果这些操作符会造成混淆,那就进行显式使用;如果不会造成混淆,就可以正常使用。整体思想是,必须确保代码是可读的且可理解的,所以和往常一样,不要自作聪明过早进行优化。
JSLint还希望循环要有花括号,所以在循环之后要有括号。循环中的单行代码不仅有可能会在后期过程中变成多行,而且还有一些静态分析工具(最小化工具、代码覆盖率生成器或其他静态分析工具,等等),如果是没有花括号的循环,也会使人感到困惑。要想为所有人(包括自己)提供便利,就应该用花括号包住循环语句。仅仅两个字节的额外“成本”将会极大地提高代码的可读性。
如下是通过JSLint验证后的代码:
for(var i=0; i< a.length; i+++)
a[i] i*i;
}
尤其令人不安的是原始代码并没有Bug!它能编译并运行得很好,会像预期一样运行。但程序是为别的程序员而编写。由于代码块在不断扩展,代码会由别人而非原作者维护(或由原作者维护,他已经不再记得自己6个月前所编写代码的意图了),因此原始代码的潜在混乱和较差可读性,可能会产生不利因素。
总结
标准的forEach方法有一个具有函数作用域的index变量,所以在初始化器里尝试声明其他的变量是没有可能的。另外,我们不需要处理++运算符,有现成的括号可以使用。利用JavaScript的“优秀”特性和惯用编程方式,可以编写更整洁以及更具有可测试性的代码。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)