JavaScript中的语句结束符

javascript是弱语言,很多方面都没有强语言要求严格。作为语句结束符的;,也是可以省略不写的。但是有些时候,省略之后是会出现问题的,而一个由于 ; 引起的问题往往很难找到,所以为了预防这样的问题也好,规范代码的书写也罢,还是养成一个每句代码结束后加上结束符(;)的习惯好。

1
2
3
4
5
6
let a,b,c
a = [1, 2, 3, 4]
a.forEach((n) => {
n *= n;
})
[b, c] = a

以上代码,乍看之下似乎没什么问题,除了最上面两行的声明与赋值之外,就是将数组a的每一项元素平方操作,然后通过数组解构赋值的方法给变量 bc 分别赋值为数组 a 的前两个元素。

但是运行起来就会发现
运行报错

神马?!居然报错了?!

不要慌,来分析下报错信息:Cannot set property 'undefined' of undefined 无法在undefined上设置属性’undefined’。这种错误熟悉不熟悉?

很明显,这种错误一般发生在给对象添加属性,而要添加属性的对象却是一个undefined值的时候。

那么问题来了:我们什么时候进行了给对象添加属性的操作了呢?

先回想一下,给对象添加属性的方法,最常用的就是点语法了object.someProperty ,另一种则是关联数组语法object["someProperty"] ,当然,还有其他方法,比如Object.defineProperty()

不过这里并不是要讨论这些,在看到关联数组语法的时候,有没有眼前一亮?没错,问题就出在这里。

1
2
3
4
5
6
let a,b,c
a = [1, 2, 3, 4]
a.forEach((n) => {
n *= n;
});
[b, c] = a

加上分号就没错了

那么原因呢?

JavaScript中之所以可以不加语句结束符,是因为你不加,系统会自动帮你加上。但是系统并不能知道你的一条完整语句具体到哪里结束,因此系统只能根据语句是否完整、能否继续等条件来判断。

很显然,a.forEach() 这条遍历语句是完整的;但是在后面的数组解构依然可以跟它连起来成为一条对象属性的赋值语句,因此系统并没有在遍历语句结束时自动地加上语句结束符。

进行步骤的拆解的话,可以这么看,记住任何语句都是有返回值的,所以不妨将a.forEach(...) 赋值一个临时变量,这样就可以更加清晰起来了

1
2
3
temp = a.forEach(...);
temp
[b, c] = a

你可能会说,这种情况很少发生的啦。没错,确实很少发生,我也是在前几天做项目的时候才初次遇到这种因为一个分号引起的错误。因为项目中使用了eslint,要求在可以使用解构赋值的情况下,必须使用解构赋值,不然就会报错。然后最初是没有这项强制要求的,而且有些人并不会特意去修改这些不影响实际代码运行的问题。而我在修改了之后才发现了这样的问题。但是我想说的是,除了这种数组解构赋值的情况,真的就没有其他情况了吗?并不是这样的吧。

比如:

1
2
3
4
5
6
7
let s1 = '1';
let s2 = '2';
+s1 // 1 => error
+s2 // 2 => error
// '12' => right

// 实际运行的是 +s1 + s2 => 1 + '2' => '12'

而且作为一个开发者,养成一个写规范代码的习惯还是很有好处的,至少我是这样认为的。