Python对换行符的处理与JavaScript的自动分号有何不同?


41

Javascript具有一种称为“自动分号插入”的功能,基本上,如果解析器遇到无效的令牌,而该令牌之前的最后一个令牌是换行符,则解析器将在换行符所在的位置插入分号。这样一来,您基本上可以编写所有不带分号的javascript代码,但是您必须了解一些边缘情况,主要是如果您有return关键字,然后要在新行中返回的值。

function test(){
    // This will return 'undefined', because return is a valid statement
    // and  "john" is a valid statement on its own.
    return 
          "john"
}

由于存在这些陷阱,因此有数十篇文章的标题为“自动分号插入是邪恶的”,“始终在Javascript中使用分号”等。

但是在Python中,没有人曾经使用过分号,并且它具有完全相同的陷阱。

def test():
    # This will return 'undefined', because return is a valid statement
    # and  "john" is a valid statement on its own.
    return 
    "john"

工作原理完全相同,但是没有人担心Python的行为。

我认为JavaScript行为不佳的情况很少,您应该可以轻松避免它们。在新行上返回+值?人们真的做了很多吗?

有什么意见吗?您在JavaScript中使用分号吗?为什么?


3
它不能完全一样地工作。在JavaScript中,分号插入不会在换行符的任何位置出现。请参阅此Wikipedia页面上的第二个示例。在该示例中,未在换行符的位置插入分号。
里德

1
我的意思不是说使用分号和不使用分号的工作原理完全相同,而是说javascript和python中的边缘情况是相同的。当然,在某些极端情况下,您必须了解正在发生的事情。我读过的关于该主题的最佳文章:inimino.org/~inimino/blog/javascript_semicolons
Einar Egilsson

4
我将分号放在JavaScript中的原因与在句子中使用句点的原因相同。当然,如果没有这些陈述,解释器通常可以理解您的陈述,但形式只是不好。
JD Isaacks 2011年

3
您可以考虑在示例中编写有效的python。注释指示符是#,不是`//'。
亚伦·杜福尔

2
“明确总是比隐性更好”

Answers:


62

原因是在Python中,换行符是一种明确的分隔代码行的方式。这是设计使然,并且已经仔细考虑了其工作方式。结果,python代码完全可读且清晰,没有任何特殊的语句结束标记(除了换行符)。

另一方面,JavaScript在设计时考虑了类似C的语法,其中语句始终以分号终止。为了使该语言更能容忍错误,它试图猜测应该在何处添加额外的分号以使代码正确。由于这是对C语法的某种改造,因此它并不总是能按预期工作(有时,脚本解释器会猜错),并且可以编写出违反直觉的代码。\

或者,用“显式优于隐式”来争论:在Python中,换行符已经是完全显式的,而在Javascript中,换行符是模棱两可的,因此您添加分号使其变得显式。


3
哦,您可以使用反引号将代码放在注释中。
tdammers 2011年

1
一个很好的例子,其中分号自动插入将最终做意外的事情是:pastebin.com/aVeWGdya
HoLyVieR 2011年

5
在python中,规则非常简单:除非以未封闭的多行字符串(“”“,'''),未封闭的dict({}),未封闭的列表([])或立即使用反斜杠,否则语句以换行符结尾在换行符之前。在javascript中,规则要复杂得多
Aaron Dufour

5
覆盖99%的错误是仅抛弃那些真正难以发现的错误的好方法。将它们留在python中是可以的,因为有简单的规则可以覆盖100%的问题。
亚伦·杜福尔

1
@Aaron:您忘记了“一组未封闭的括号(())”。(因为括号不仅仅用于元组,所以不严格地说是“未封闭的元组”。)
JAB

28

我认为,它与Python的工作方式有很大的根本差异。引用自Einar Egilsson的帖子的链接是:“如果可以将下一行的第一个标记解析为同一语句的一部分,则该行的末尾不暗含分号”。

在Python中,换行符总是结束该语句,除非在某些相当明显的情况下(例如在带括号的表达式中)。另一方面,JavaScript将尝试在结束该语句之前解析尽可能多的行,从而可能导致如下情况:

// Define a function and name it area.
area = function(r) {
    return r * r * 3.14159
}

// Fooled you! We're actually invoking it.
(14)

8
现在,这是一个有趣的转折。将14替换为类似的内容,(a + 1) ? do_something() : do_something_else();然后突然将area设置为do_something()or 的返回值,do_something_else()就让您感到困惑。
里德(Reid)

20

我经常在生产模式下最小化我的JS文件。意思是,删除注释和换行符。

如果不使用分号,那将破坏我的Javascript。


8
好,那是有效的。但是,如果您的最小化器是实际的解析器,则可以根据需要再次插入它们。或者只是不删除换行符,=>保留换行符,丢失分号,它们的数目大致相同,因此不会丢失任何内容。
Einar Egilsson

2
@Einar Egilsson Closure Compiler确实可以做到这一点。
严重dev11年

1
在所有条件都相同的情况下,换行符与分号的字节数相同。最小化器可能会删除所有新行,但随后需要有分号。这是一次公平的交换。
Logan Bailey

3
@Logan:当然,这是假设一个字节的换行符;-)
Cameron

1
当然,如果我们使用Python而不是Javascript,则必须减少编写方式来完成同一件事,因此分号使用比缩进少几个字节的事实是有争议的。
BlueRaja-Danny Pflughoeft

5

它不能像您描述的那样工作。

Javascript具有一种称为“自动分号插入”的功能,基本上,如果解析器遇到无效的令牌,而该令牌之前的最后一个令牌是换行符,则解析器将在换行符所在的位置插入分号。

错了 例:

return
  1 + 2;

1是一个完全有效的令牌,但解析器仍会在之后直接插入分号return

如您所见,即使您也无法确切知道分号将在哪里发生。

自动插入的问题有两个:

  • 例如,人们可能会忽略分号,在该分号中自动插入无法确定是否需要插入一个分号。
  • 而且,如上所述,分号可能会插入到不需要的地方。

当然,在每个语句后使用分号仅有助于解决第一个错误源。

无论如何,就像您现在可能猜到的那样,我认为以C形语法自动分号插入是一个坏主意。


1
ECMA脚本规范明确指定了要插入分号的情况,因此您的行“您无法确切知道分号将在何处发生”是不正确的。问题是,在某些情况下它是不直观的,这使得教导不了解它的工作原理的人变得更加困难。
zzzzBov 2011年

1
@zzzzBov:是的,有一个确切的规范,但是有人在编码时真的想到所有情况吗?你确定吗?程序员是懒惰的,理应如此。他们不想记住一个更简单的规则就可以记住的复杂规则。因此,他们尝试绕过必须记住的地方。
斯万特·

我同意在很大程度上不需要插入分号。我只是说“您不知道分号要去哪里”和“分号插入的规范是一个不直观的混乱”之间有
区别

1
@Svante:但是返回示例向我们展示了我们仍然必须了解这些规则。在那里,您使用了分号,但并没有帮助您完成所需的操作。因此,鉴于该语言具有此功能,我们可以选择(1)在各处编写分号并了解规则,以便我们了解将要发生的事情(2)不要在各处编写分号并了解规则以使我们了解将要发生的事情。考虑到这种选择,我想我宁愿跳过分号
Einar Egilsson

4

我要说明一个简单的原因:

Javascript看起来像“ kinda java-ish”或“ kinda C-ish”。当然,它是一种动态语言,因此它看起来有所不同...但是要面对它- 有括号。带花括号的语言通常带有分号。自然的反射声响起,使您的手指在敲击前指向分号键Enter

相反,即使乍一看,Python看起来也完全不同。因此,几乎没有或根本没有类似于“标准无聊语言”的类比,当人们进入“ python模式”时,缺少分号是很自然的。


2

有很多充分的理由不在 JavaScript中使用分号插入。

主要是因为ECMAScript标准中定义的分号插入在某些情况下不直观。@Svante指出了return换行用法会导致问题的情况。

他没有提到的是,如果同时使用分号,也会引起问题,因为无论是否要插入分号,都会发生。

使用分号插入的另一个很好的理由是输出控制。在许多情况下,JavaScript在生产中使用之前都要经过一个minifier。某些压缩程序可能会处理分号自动插入的情况,但我认为没有理由依靠它来完美运行

此外,对于内容管理系统,嵌入式JavaScript可能会自动缩小,并且我已经看到许多情况,其中自动缩小器只是删除注释并修剪每行开头和结尾的空格(包括换行符)。

对于无法选择工具的作者而言,坚持适用于绝大多数情况下的格式要容易得多。


啊,很抱歉,但是关于您的第三段,我的倒数第二句话确实提到了这一点。:)
Svante

是的,工具问题是有效的(尽管优秀的压缩工具应该可以解决此问题,例如Closure编译器)。但我认为,无论如何我们都需要了解这些规则,以避免出现“返回”示例之类的事情。而且,一旦我了解了规则,我就不妨使用该功能,特别是因为它使代码(IMO)更具可读性。
Einar Egilsson

1

最小化JavaScript文件时,不使用分号会导致失败。这就是为什么我对此感到恐惧。


1

在Javascript中,您可以编写一个在没有自动分号插入的情况下在语法上正确的程序,ASI会将其转换为在语法上正确的另一个程序(例如,将返回值的代码转换为不返回任何代码的代码)。Python中没有类似的情况。在Python中,任何可以结束一条语句的换行符结束一条语句,除非用反斜杠将其转义。从技术上讲,我认为Javascript的规则同样具有确定性,但是我不知道您是否可以在一个句子中总结Java语言的规则来结束语句。


1

在大多数情况下,JavaScript的ASI可以按预期进行处理。ASI的一个示例可能不符合您的预期方式:

var i = 0

(function() {
   // do something
})()

这将被解释为0使用匿名函数调用该函数,然后执行结果。在这种情况下,您可能想进行分配,然后立即执行匿名函数。

对于不熟悉ASI的人,当您遇到这样的问题时可能会非常混乱,因此我始终建议团队中的开发人员使用分号。

(顺便说一句:在个人/副项目上工作时,我不使用分号,因为我知道没有其他人需要维护代码。)


1

像你一样,我觉得这有点偏执。用JavaScript很好地定义了分号插入的规则,就像在Python和CoffeeScript中一样。没有人会将Python或CoffeeScript放在分号上,那么为什么对JavaScript进行不同的对待?

我认为这与大约10年前典型的JavaScript代码的糟糕状态有过分的反应-JavaScript被认为是一种软弱的,有bug的,丑陋的,不好的语言。真是尴尬。您不可能用JavaScript编写好的代码!

然后,人们出现并试图证明您可以用JavaScript编写漂亮,清晰的代码。“ 始终使用分号”规则是这一浪潮的一部分。老实说,它可以使某些情况更加清晰。

为什么JavaScript仍会被区别对待?

有惯性。而且,欣赏显式结构化代码的人们通常更喜欢C风格的语言,这一点也不容忽视欣赏隐式结构化代码的人通常会转而使用非C风格的语言(例如CoffeeScript)。


0

我严格使用Javascript来保持一致性。如果大多数行都有

Python将它们用于边缘情况,例如一行上有多个语句,而javascript具有它们,并且由于您会发现它们经常使用,因此我符合使用它们的规范。

我找不到同一行上多个语句的用处,因此无法预见使用分号。


是的,我已经修复了Python示例。但是重点仍然是,Python也有分号,欢迎您在每条语句后加上分号(如果每一行中有多个分号,则必须这样做),但是人们不使用它们。
Einar Egilsson

0

如果您在web应用程序中使用bundle-fu和资产管理器之类的工具,那么如果在javascript令牌的末尾没有遇到分号,它将很糟糕。因此,放一个是一个好习惯。


好吧,YUI Compressor,Closure Compiler和UglifyJS这三者都进行分号插入。我对JSMin的ruby端口有问题并不感到惊讶。
本杰明·阿特金

0

我不记得IE的确切版本,但是在某些情况下,如果缺少分号,则IE确实会出错。IIRC是在全球范围内的情况,例如:

var myFunc = function() {
  ...
}

如果不添加; 大括号后,该程序实际上将在某些版本的IE上失败。这以及其他原因(包括克罗克福德建议始终明确使用它们的原因)使我在每种情况下都始终明确使用它们。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.