/ 闭门造轮子 / 一个分号引发的血案

一个分号引发的血案

2012-07-30 posted in [问题即经验]

Hadson集成CI环境出同一个问题好多次了,原因却各不相同。今天再次碰到,纠结了很长时间依然不明白,为什么版本差异仅仅是提交的JS代码,就导致整个项目编译失败?dulin同学再次仔细的查看日志后发现报错的仍是yuicompressor的压缩步骤,但不同的是居然因为JS检查中syntax error导致无法继续。

[ERROR] .../src/main/webapp/assets/js/~build.js:line 2931:column 6:missing ; before statement
}esui.Button.prototype.__createMain = function () {
[ERROR] .../src/main/webapp/assets/js/~build.js:line 2933:column 1:syntax error
};

真正跟到这条日志的时候就很明了问题出在什么地方了——分号

我写的前端JS合并打包的过程会按序列拼接所有的JS文件,那么其中可能有的文件就是一整个对象声明,而在最尾忘记了写分号,然后跟下一个文件合并的时候打包脚本又不会自动补一个换行符,这就导致原本开发时都是分散的文件没有任何问题,但合并后就出现了日志中这种情况:

var a = {}b = 100;

于是yuicompressor的JS语法分析器到这就无法辨别这究竟是一个什么语句,最终报出syntax error。

更详细的原因可以参照ECMA-262语言规范中分号自动插入规则(原版地址:http://bclary.com/2004/11/07/#a-7.9.1)来解释:

  • 从左到右解析程序时,若遇到不被任何产生式允许的token(被称为/违规token/),于是,在下列情况一个或多个为真的违规token之前自动插入一个分号:
    1. 该违规token与前一个token之间以至少一个行结束符分隔开。
    2. 该违规token是}

就是说,如果上面日志中的错误例子中有一个换行符,那么解释器是会自动插入一个分号的:

var a = {};
b = 100;

但如果没有,解释器就不知道怎么办了,明显{}b这样的表达式也读不通,所以报错了。

更具体的分号处理还是看ECMA的规范吧,当然这次问题的原因也导致了我对《JavaScript语句后应该加分号么?》这篇文章的不认同。在应该用分号结束一个语句的地方不加,刚好又在文件结尾没有换行符,当多文件合并时就必然出现这样的问题。而没加的原因正是在有团队代码规范时仍有程序员的代码习惯不好。这种情况甚至让某些团队出台JS文件第一个语句必须以分号开头的规范。所以我个人还是支持每一条语句都以分号结束,但不需要极端到开头写分号。

这次问题排查时间过长也有一部分之前yuicompressor的maven插件其他问题的干扰,但根本的还是换到Sublime Text下开发没有强制团队使用语法检查的工具。而之前一直用Aptana时每个错误都会实时提示,让人不能不主动解决掉。然后搜索了之前装的SublimeLinter不能跑起来的原因是没有装node.js,装了之后果然好用了。所以今后需要在几个方面都有所注意:

OK,结案!

-EOF-