vb.net label 不要自动换行_从底层看前端(十)—— JavaScript语法:到底要不要写分号?...

0eda40d0c53a1ba5376721e70f189f9c.png

从这篇文章开始,我们来聊一聊JavaScript的语法部分。

首先我们来探讨一个语言风格问题:

究竟要不要写分号?

这是一个非常经典的口水问题,分号党和不加分号党之间的战争,可谓是经久不息。

实际上,行尾使用分号的风格来自于Java,也来自于C和C++,这一设计最初是为了降低编译器的工作负担。

但是,从今天的角度来看,行尾使用分号,其实是一种语法噪音,恰好JavaScript语言有提供了相对可用的分号自动补全规则,所以,很多JavaScript程序员都是倾向于不加分号的。

这里需要说明一点,今天的文章中,我并不希望去售卖自己的观点,而是希望比较中立地和你一起探讨相关知识,让你具备足够的判断力。

首先我们来了解一下插入分号的规则。

自动插入分号规则

自动插入分号规则其实独立于所有的语法产生式定义,她的规则说起来非常简单,只有三条:

要有换行符,且下一个符号是不符合语法的,那么就尝试插入分号。
有换行符,且语法中规定此处不能有换行符,那么就自动插入分号。
源代码结束处,不能形成完整的脚本或者模块结构,那么就自动插入换行符。

这样描述是比较难以理解的,我们一起看一些实际的例子进行分析。

let a = 1
void function(a){
	console.info(a);
}(a)

在这个例子中,第一行结尾处有换行符,接下来 void 关键字接在 1 之后是不合法的,这里命中了我们的第一条规则,因此会在 void 前插入换行符。

var a = 1,b = 1, c = 1;
a
++
b
++
c

这也是个著名的例子,我们看到第二行的a之后,有换行符,后面遇到了++运算符,a后面跟++是合法的语法,但是我们看看JavaScript标准定义中,有 [no LineTerminator here] 这个字样,这是一个语法定义中的规则,你可以感受一下这个规则的内容。

UpdateExpression[Yield, Await]:
    LeftHandSideExpression[?Yield, ?Await]
    LeftHandSideExpression[?Yield, ?Await][no LineTerminator here]++
    LeftHandSideExpression[?Yield, ?Await][no LineTerminator here]--
    ++UnaryExpression[?Yield, ?Await]
    --UnaryExpression[?Yield, ?Await]

于是,这里的 a 的后面就要插入一个分号了,所以这段代码最终的结果,b 和 c 都变成了2,而 a 还是1。

(function(a){
    console.log(a);
})()
(function(a){
    console.log(a);
})()

这个例子呢是一个比较有实际价值的例子,这里两个 function 调用的写法被称作IIFE(立即执行函数表达式),是个常见的技巧。这段代码的意图是形成两个IIFE。

我们来看下第三行结束的位置,JavaScript引擎会认为函数返回的可能是个函数,那么,在后面再跟括号形成函数调用就是合理的,因此这里不会自动插入分号。

这也是一些鼓励不写分号的编码风格会要求大家在写 IIFE 时必须在行首加分号的原因。

function f(){
    return/*
        This is a return value.
    */1;
}
f();

在这个例子中,return 和 1 被用注释分开了。

根据JavaScript自动插入分号规则,带换行符的指数也被认为是有换行符,而恰好的是,return 也有 [no LineTerminator here] 的规则要求。所以这里会自动插入分号,f 执行的返回结果是 undefined。

no LineTerminator here 规则

好了,到这里我们已经讲清楚了分号自动插入的规则,但是我们想要彻底掌握分号的奥秘,就必须要对JavaScript的语法定义做一些数据挖掘工作。

no LineTerminator here 规则表示它所在的这一位置不能插入换行符。

自动插入分号规则的第二条:有换行符,且语法中规定此处不能有换行符,那么就自动插入分号。跟 no LineTerminator here 规则强相关,那么我们就找出JavaScript语法定义中的这些规则。

d481ac2e97fdaed87a13eb9074ec6e61.png

为了方便理解,这里我把产生式换成了实际的代码。

下面这段代码展示了,带标签的 continue 语句,不能在 continue 后插入换行。

outer:for(var j = 0; j < 10; j++)
    for(var i = 0; i < j; i++)
        continue /*no LineTerminator here*/ outter

break 和 continue 一样,break 后也不能插入换行符。

outer:for(var j = 0; j < 10; j++)
    for(var i = 0; i < j; i++)
        break /*no LineTerminator here*/ outter

前面已经提到过的 return 和后自增,后自减运算符。

function f(){
    return /*no LineTerminator here*/1;
}

i/*no LineTerminator here*/++
i/*no LineTerminator here*/--

以及,throw 和 Exception 之间也不能插入换行符。

throw/*no LineTerminator here*/new Exception("error")

凡是 async 关键字,后面都是不能插入换行符的。

async/*no LineTerminator here*/function f(){

}
const f = async/*no LineTerminator here*/x => x*x

箭头函数的箭头前,也不能插入换行符。

const f = x/*no LineTerminator here*/=> x*x

yield 之后,不能插入换行:

function *g(){
    var i = 0;
    while(true)
        yield/*no LineTerminator here*/i++;
}

到这里,我们已经整理了所有标准中的 no LineTerminator here 规则,实际上,no LineTerminator here 规则的存在,多数情况是为了保证自动插入分号行为是符合预期的,但是令人遗憾的是,JavaScript在设计最初,遗漏了一些重要的情况,所以会有一些不符合预期的情况的出现,需要我们格外注意。

不写分好需要注意的情况

下面我们来看几种不写分好容易造成错误的情况,你可以稍微注意一下,避免发生同样的问题。

以括号开头的语句

我们在前面的例子中,已经展示了一种情况,那就是以括号开头的语句:

(function(a){
    console.log(a);
})()/*这里没有被自动插入分号*/
(function(a){
    console.log(a);
})()

这段代码看似两个独立执行的函数表达式,但是其实第三组括号被理解为了传参,导致抛出错误。

以数组开头的语句

除了括号,以数组开头的语句也十分危险。

var a = [[]]/*这里没有被自动插入分号*/
[3, 2, 1, 0].forEach(e => console.log(e))

这段代码本意是一个变量 a 赋值,然后对一个数组执行 forEach,但是因为没有自动插入分号,被理解为下标运算和逗号表达式,这个例子展示的情况,甚至不会抛出错误,这对于代码排查来说简直是个噩梦。

以正则表达式开头的语句

正则表达式开头的语句也值得你去多注意一下。

var x = 1, g = {test:()=>0}, b = 1/*这里没有被自动插入分号*/
/(a)/g.test("abc")
console.log(RegExp.$1)

这段代码的本意是声明三个变量,然后测试一个字符串中是否含有字母a,但是因为没有自动插入分号,正则的第一个斜杠被理解成了除号,后面的意思就变了。

这个例子跟上面的一个例子一样,同样不会抛出错误。

以Template开头的语句

以Template开头的语句比较少见,但是跟正则配合时,有时候仍然会出现错误。

var f = function(){
  return "";
}
var g = f/*这里没有被自动插入分号*/
`Template`.match(/(a)/);
console.log(RegExp.$1)

这段代码本意是声明函数 f,然后复制给 g,再测试 Template 中是否含有字母 a,但是因为没有自动插入分号,函数f被认为跟 Template 一体的,进而被莫名其妙地执行了一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值