JavaScript: 声明、表达式和语句的区别

15
在过去的几个小时里,我一直在尝试找出这三者之间的区别,不仅如此,我还一直在尝试找出它们之间的同义词。MDN将所有声明称为"语句",因此我认为这是正确的。然而,我阅读的所有文章和SO问题都没有提供一个类似cheatsheet的东西来区分这三个(或两个,表达式与语句)。
我注意到关于语句的一点是它们都以某种特殊的JavaScript关键字比如 breakfor 或者 var 开头。文章说一个表达式会计算得到一个值,而语句则执行一个操作。那函数呢?它是一个语句-表达式混合体吗(因为当调用时,它既执行一个动作又返回一个值)?我现在假设不是这样,因为函数调用并不涉及JavaScript关键字。
然后还有声明,每个声明也是一个语句吗?
我也知道像 20 + 5 * 2 / 3; 这样的表达式语句,但这些几乎没用,为什么会有人用这样的代码污染他们的代码库呢?
如果有人能回答上述问题(或至少其中一些),或者如果有人能为我提供某种cheatsheet,我将非常高兴。为什么在这样一门流行的语言中出现的一个看起来如此简单的主题上有如此少的材料呢?

我也知道像 20 + 5 * 2 / 3; 这样的表达式语句,但这些几乎是无用的吧?你可能已经看到过以下两种表达式语句,并且不认为它们是无用的:变量赋值 (foo = 42;) 和函数调用 (bar();)。 - Felix Kling
1
不好意思打广告:http://astexplorer.net/ 可以检查JavaScript程序的AST。 - Felix Kling
谢谢!<3 我现在正在用我的笔记本打印一些东西,但我对你的第一条评论也有很多话要说,过几分钟会回来。 - doubleOrt
1
现在已经过去了三年多,但Felix仍在等待。谁知道发生了什么事?也许打印工作从未停止。也许doubleOrt最终决定编程并不是他们想要追求的人生道路。也许他们骑着白马,在日落中消失了。我们永远不会知道。 - Niels Bom
1
@NielsBom 我最近才重新开始编程,不知道 JavaScript 的情况有没有变化。两年前,React 和 Angular 是最流行的框架,现在情况怎么样了? - doubleOrt
查看 Stack Overflow 开发者调查 - Niels Bom
3个回答

18
这些术语都有标准定义,包括JavaScript在内的所有编程语言都遵循这些定义。对于它们,我的理解如下:
  • 表达式(expression)生成一个值,并且可以在需要值的任何地方编写。

  • 语句(statement)是一个独立的执行单元,不返回值。

  • 声明(declaration)是一种将值分配给变量的语句。

  • 所有声明都是语句,但并非所有语句都是声明。

  • 大多数语句和所有声明都包含表达式。

Javadoc也提供了简洁明了的定义:

表达式是由变量、运算符和方法调用构成的结构,在语言的语法规则下计算为单个值。

语句与自然语言的句子相似,是完整的执行单位。

现在,你关于关键字必须参与的理论是不正确的。以下是包含关键字的语句:

break;

这里有一个不这样做的例子:

foo();
在第一个示例中,我执行了一个break语句,而在第二个示例中我调用了一个函数。两者都是语句,但只有一个包含任何特殊关键字。
函数不是一个语句。如上所示,函数调用是一个语句。对于返回某些内容的函数,该调用还可以是一个声明:
var bar = foo();

至于你最后一个问题,之所以关于这个问题的材料很少,是因为这只是一个语义问题。人们不会过多纠结于这些术语的确切定义。

为了进一步澄清,我建议查看Axel Rauschmayer的这篇博客文章,具体讨论JS中语句和表达式的区别。


为什么函数不是一个语句?它难道不只是一个声明吗? - doubleOrt
1
该函数本身可以被视为声明,但这取决于编程语言。 - stelioslogothetis
我假设 foo (); 是所谓的“表达式语句”。如果是这样,那么除了这些表达式语句之外,还有其他不包含关键字的语句吗? - doubleOrt
另一个评论回答了我的最后一个问题,块语句也不涉及关键字 :) - doubleOrt
1
在JavaScript中,声明不是语句,尽管它们通常被类似地处理。 - Felix Kling
显示剩余7条评论

6
在过去的几个小时里,我一直试图找出这3种语法结构之间的区别,不仅如此,我还试图找出哪些是同义词。MDN称所有声明为“语句”,因此我认为这是正确的。
声明是在编译/加载时将变量名“声明”为存在,并在程序执行之前。因此,在其各自的作用域中声明的所有名称都是预先知道的。
语句和表达式之间的区别在于前者不会产生值,而后者会产生值。因此,表达式可以在期望值的任何地方使用,而语句则无法在这些位置使用。
表达式语句是其中语句是单个表达式或多个包含在需要零个或多个子表达式的表达式中。虽然表达式(们)会产生结果,但语句不会。
下面是一个表达式:
x = foo() + 2

如果在结尾处添加分号,就可以明确地显示完整的表达式语句。

x = foo() + 2;

第一个例子可以用一组括号包含起来,因为括号作为分组运算符需要一个表达式,通常由逗号运算符连接的几个表达式提供。
第二个例子不能用括号包含(如果包含分号),因为它是一个语句,不产生一个值,而分组运算符期望接收并返回其自己的值。
引用块中说:“我注意到语句都涉及到JavaScript关键字,如break、for或var。”
虽然大多数语句确实涉及关键字,但并非所有语句都是这样。最值得注意的是,块语句没有关键字。相反,它使用一组花括号来界定其开头和结尾。
文章说,表达式会计算出一个值,而语句执行一个操作。那函数是什么呢?它是一个语句-表达式混合体(因为调用时既执行操作又返回一个值)吗?目前,我认为这不是这种情况,因为函数调用不涉及JavaScript关键字。
在JS中有三种类型的函数。如果你谈论使用function关键字的函数,则根据其上下文可能是声明或表达式。
如果函数用在期望表达式的地方,则将其评估为返回新函数对象的表达式。如果不是,则期望它是一个声明,它基本上创建一个变量,并将函数对象分配给它。作为声明,函数名是必需的(用于变量名)。作为表达式,它是可选的。
我也知道像20 + 5 * 2 / 3;这样的表达式语句,但这些几乎是无用的吗?为什么有人要在代码中添加这样一行?
如果忽略结果值,则给出的例子将是无用的。但是这里还有另一个表达式语句。
foobar();

现在我们从函数调用中得到了一个副作用,这很可能是期望和有用的。

2
顺便说一下,我看到了语句和声明之间的区别,其中语句是运行时的一部分,而声明发生在任何代码执行之前只有一次。有人可能会说它们是相同的,因为执行多次的函数将需要多次创建已声明的变量,但是虽然来自声明的信息可能每次执行函数时都被利用,但声明本身发生在程序被解析时,远在任何代码运行之前。这些声明中的信息在设置执行上下文时每次提供。 - llama
增加混淆的是,var a;var a = 2; 有什么区别?我会认为,第一个是一个“声明”。在第二个中,发生了两件事:声明赋值,整体上是一个“语句”。没有“表达式”,只有一个“值”。如果我写了 var a = 2 + 1;,那么我们就有了一个“声明”,一个“表达式”和一个“语句”。我错了吗? - akinuri
转念一想,如果我们将“独立执行单元”(上文)称为语句的定义,那么var a;既是声明,也是语句。因此,语句似乎是比声明/表达式更通用/更高级的概念。 - akinuri

2
首先让我们谈论表达式。正如你所说,表达式是一个求值为值的东西。函数是一个值。另一方面,函数调用是一个表达式。
我不同意将语句描述为“执行操作”的表述。正如你所说,调用函数涉及运行函数体,其中包含语句和声明,因此它在“内部”执行操作。
更好的看待它的方式是,表达式、语句和声明是语法类别。它们起源于试图描述JavaScript语法结构。例如,请参见ECMAScript 8规范中有关表达式、语句和声明的部分。
像20 + 5 * 2 / 3;这样的表达式语句确实是无用的,因为它没有任何效果,而语句的唯一目的就是其效果。但是还有其他更有用的表达式语句:
- 函数调用:foo(); - 赋值:x = 42;(=是运算符,可以用于表达式) - 增量/减量:i++;(类似地,++是运算符)
除表达式语句外,其他语句通常以特殊关键字开头,以避免语法歧义:解析器希望从一开始就能够确定它正在处理哪种结构。因此我们得到:
- if语句:if (EXPR) STMT - while语句:while (EXPR) STMT - return语句:return; 或 return EXPR; - 等等。
然而,有一些语句不以关键字开头:
- 空语句:; - 一个块:{ ... }
最后是声明。在JavaScript中,语句和声明之间的区别有点任意。语法区分了这两者,但在实践中,你通常可以将它们视为相同的(例如,块语句的内容被定义为一系列“语句列表项”,而语句列表项被定义为语句或声明)。声明包括:
  • 函数声明:function foo() { ... }(包括生成器函数(function* foo() { ... })和异步函数(async function foo() { ... })等变体)
  • letconst
  • class 声明

(但由于某种原因,var foo; 被归类为语句而不是声明。)


你确定你回答的结尾部分是 var 吗?请参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var 和 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements#Declarations。 - doubleOrt
@Taurus 是的,请参见 http://www.ecma-international.org/ecma-262/8.0/index.html#prod-VariableStatement。 - melpomene
奇怪,你有什么想法为什么分号是一个语句吗?这真的比我想象的更奇怪。 - doubleOrt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接