JS省略分号导致的灾难

mattuy 2020年05月05日 237次浏览

挺长一段时间在纠结写JavaScript代码要不要打分号。这是一个个人风格问题,以下观点仅代表个人喜好。

不要省略分号!不要省略分号!不要省略分号!

虽然js引擎执行代码时会自动插入分号,但有些时候省略分号可能导致一些隐蔽的问题。

return语句

return后面会被自动插入分号,所以下面的代码返回undefined而不是一个字符串。

function foo() {
    return 
    "this will return undefined"
}

当然,这完全可以避免,我们可以将返回值写在一行或者用括号包起来。

function foo() {
    return (
        "this is ok."
    )
}

似乎这点也不是不省略分号就能避免的,如果您不清楚这条规则,下面的代码可能会带来疑惑:

function foo() {
    return
       'a long expresion' +
       'another long expresion';
}
foo(); // undefined

OK,这不是重点,事实上现代编辑器足够聪明,这样的情况应该会将return后面的语句给你标注为灰色,你很容易就可以看到它们没有正确的被执行。这里只是顺带一提。我们来看更隐蔽的坑。

函数执行

考虑下面的代码:

var foo = 0
var bar = 1

foobar()

('some condition' && 'another condition') ? foo = -1 : bar = -1

console.log(foo, bar)

function foobar() {
    return function () { return false }
}

请问上面的代码输出什么?答案是0 -1

我们的本意是在两个条件满足时给foo变量赋值-1,否则给bar变量赋值-1。这里本该输出-1 1,但由于语句以括号开头,js执行引擎误以为上一行的语句还没有结束,正好foobar()返回了一个函数,于是上面的代码相当于(为了更清晰我引入了一个变量):

var condition = foobar()('some condition' && 'another condition');
condition ? foo = -1 : bar = -1;

foobar()返回的函数总是返回false,所以被赋值的总是bar变量,这完全改变了我们的初衷!而且编辑器不会有提示:没有语法错误。甚至运行时也不会直接出错,而是在某个case下才命中bug。

解构赋值

接下来是我今天亲身经历的的场景。

ES2015(ES6)新增了一个解构赋值的特性,用于交换两个变量的值那是非常的方便。例如交换a,b变量的值:[a, b] = [b, a]。当然解构赋值还有很多其他特性,可以参考MDN

考虑如下代码:

var m = n = 0;
var swap = -1;
var matrix = [
    [1, 2],
    [3, 4]
];

//do something...

[matrix[m][n], swap] = [swap, matrix[m][n]]
[m, n] = [n, m]

console.log(matrix) // [[0, 2], [3, 4]]

OK,我们不用管代码是要做什么,反正就是想交换matrix[m][n]swap的值,再交换mn。期望的输出应该是[[-1, 2], [3, 4]]。然鹅...

事实上在写这篇博客之前我是赞成省略分号的。前面两种情况我都习惯性会注意避免,也没出过类似的错误。但终于还是不留神栽在了解构赋值上。

还是解释一下,上面的解构赋值语句等价于:

[matrix[m][n], swap] = [swap, matrix[m][n]][m, n] = [n, m]

//不太清晰?由于m == n == 0,逗号表达式m,n的值为0,所以再等价于:
[matrix[m][n], swap] = [swap, matrix[m][n]]   [0] = [n, m]
//即
[matrix[m][n], swap] = swap = [n, m]
//即
[matrix[m][n], swap] = [n, m]
//即
maxtrix[0][0] = 0;
swap = 0;

同样,由于没有语法错误,IDE同样不会检测到。

总结

暂时没有遇到其他情况,不过有时候是防不胜防。咱一朝被蛇咬,十年怕井绳。添上分号他不香嘛。