JavaScript:语句和表达式的区别?

100

我之前在这里提出了一个问题,经过思考,我开始想知道术语 “语句” 和 “表达式” 之间似乎模糊的边界在哪里。所有语句都是表达式吗?REPL控制台中的返回值从何而来?它们并不总是有直观意义的。当然,如果你输入 1+1,你会得到 2,但其他时候逻辑并不那么明显。

鉴于任何输入到 REPL 中的内容都会产生一些值,那么这是否意味着它既可以作为表达式又可以作为独立语句在JS源代码中使用?

下面的代码片段中,可以用于 _X_ 的代码字符串是否也可以用于 _Y_,反之亦然?if(_X_) _Y_?


你可以使用 eval 函数来执行 JavaScript 代码,这让我认为在 JavaScript 中所有语句都是表达式。 - Sidharth Mudgal
“REPL控制台中的返回值从哪里来?”--来自完成记录。另请参见为什么这个do-while循环会在结束后重复最后一个值? - Sebastian Simon
11个回答

68

所有语句都是表达式吗?

在JavaScript中,无论何时需要一个语句,你都可以编写一个表达式。这样的语句被称为表达式语句。反之不成立:你不能在JavaScript期望表达式的地方编写语句。例如,if语句不能成为函数的参数。

这篇最近由Axel Rauschmayer发表的文章详细探讨了这个主题: JavaScript中的表达式与语句

希望这有所帮助。


5
以下是有关 JavaScript 语句和表达式的进一步阅读资料,即差异、关键词等方面的信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators 和 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements - Adriano
除非您将其括在括号中,否则无法将有效表达式{"x": 2}编写为语句。 - Sebastian Simon

62
根据MDN的说法:

表达式是任何可以解析为一个值的有效代码单元。

因此,任何可以用作右值的东西都是表达式。
标准并不是副作用是否存在。 表达式肯定可以具有副作用。 例如,a=2 是一个表达式,因为它有一个值(2),并且还将一个值赋给变量。 这就是为什么你可以做这样的事情:
let a;
let b = 1 + (a = 2); // a is now 2 and b is 3

同一个(文本)代码块可以根据上下文同时被视为表达式语句。例如,文本片段function f(){}在以下代码中的第1行是表达式,在第2行是语句:

let g = function f() {};
function f() {};

因此,无法通过只查看文本代码片段来确定某个内容是表达式还是语句(在一般情况下);相反,它是语法树中节点的属性,并且只有在代码被(精神或实际)解析后才能确定。

此外,也更重要的是,函数内部的函数语句(也称为函数声明)形成了执行上下文的一部分,该执行上下文在调用函数时创建。然而,函数表达式不会成为该执行上下文的一部分。

经常引用的一个影响是函数声明会被“提升”,而函数表达式则不会。

还可以在深层递归中通过实验观察到更微妙的影响,因为函数语句在执行上下文中占用空间,而函数表达式则不会。例如,以下代码使用了函数 f 的无限递归。第一种情况下,函数 f 包含其中一个函数表达式,而第二种情况下,它包括了等效的函数声明:

// Function Expression
{
    let i = 0;
    try {

        function f () {
            i++;
            (function g() {})(); // this is an expression
            f();
        }

        f();

    } catch (err) {
        console.log(`Function Expressions case: depth of ${i} reached. Error: ${err.name}`);
    }
}
// Function Declaration
{
    let i = 0;
    try {
        function f () {
            i++;
            function g() {}; // this is a statement
            g();
            f();
        }

        f();

    } catch (err) {
        console.log(`Functions Declarations case: depth of ${i} reached. Error: ${err.name}`);
    }
}

在我的电脑上,我持续地得到以下结果(在node.js中):

Function Expressions case: depth of 17687 reached. Error: RangeError
Functions Declarations case: depth of 15476 reached. Error: RangeError

…这与函数声明会增加执行上下文所需空间并因此稍微更快地消耗堆栈空间,从而略微降低最大递归深度的事实是一致的。


1
非常好的回答!谢谢。 - Davi Lima
深度差异来自于变量数量的不同。语句风格在作用域中引入了一个新变量g,表达式风格则没有。 当在表达式前添加"let g="时,深度将会相等。 - Lauri
@Lauri 执行上下文是保存本地变量的东西。函数表达式不会添加任何内容到执行上下文,而函数声明会。另请参见:https://dev59.com/5Wox5IYBdhLWcg3wLhQA#9384894 - Marcus Junius Brutus
2
这应该是被接受的答案。它实际上回答了OP问题的所有部分,并且做得非常好。 - 223seneca
语句和声明是一样的吗? - Doc Kodam

37

简单来说,表达式被计算以产生一个值。而语句则被执行以实现某种操作


1
一个语句也可以被评估以产生一个值,例如 var x = makeSomethingHappen();,因此这个答案是不正确的。(你读了被接受的答案吗?它已经解决了这个问题。) - Mogsdad
4
请纠正我,但这不是被认为是“表达式语句”吗?我理解一个语句也可以被评估以产生一个值,就像你上面演示的那样。但是,表达式和语句之间的区别可以总结如上所述吗? - u-ways
当然可以,但是这个回答并没有提供任何被接受的答案中没有涵盖的内容。 - Mogsdad
3
除了被广泛接受的答案最好是具有误导性的,至少这个回答不会误导。 - user234932
1
@Mogsdad 你错了。如果你更仔细地阅读了Marcus所说的“因此,无论某个东西是表达式还是语句,在文本代码上下文外观察不能(在一般情况下)确定;相反,它是一个语法树中节点的属性,并且只能在代码被(心理或实际)解析之后确定。”那么你就会意识到var x = makeSomethingHappen();不会产生任何值,因为var x = whaterver不会返回任何值,只有makeSomethingHappen()会返回值,但同样,从以上语句中读取... - stackoverflow

21

表达式产生或计算某个值。

表达式的示例: new Date() 会产生新的日期对象而不会产生任何副作用。 [1,2,3] 会产生一个新的数组而不会产生任何副作用。 5+6 会产生一个新的值11。它只是产生了一个新值,没有任何副作用。

语句产生一些行为或执行某些操作,并且它也具有一些副作用。 基于副作用,语句可以被分类。

x=5; 是一条语句,这里的副作用是赋值或更改x。

setTimeout() - 计时器的启动是副作用。

语句通常由分号分隔。

表达式语句是具有某些副作用的表达式,或者简单地说是“具有副作用的表达式”。

表达式语句的示例:

x+=6; 是一个复杂表达式(一组主要表达式),正在进行赋值,这就是所谓的表达式语句。

delete a[2];

9
x=5; 是一个"表达式语句"。x=5(没有分号)是一个表达式。产生行为或者是否执行某些操作并不是定义标准,详见我的答案。 - Marcus Junius Brutus
1
setTimeout()是一个表达式。它甚至会产生一个值。delete a[5]也是一个表达式。“表达式产生或评估某些值。”-你会认为new Array(-1)JSON.parse("x")(function(){"use strict"; x = 2;})()() => {await 3;}是表达式吗?它们都会抛出错误。 - Sebastian Simon

15

表达式: 代码单元,可解析为值,如文字常量运算符。表达式示例:

> 3
> 1 + 2
> "expression"
> fn()
> []
> {}


语句: 表示一个或多个指令的代码单元,通常以语言保留关键字开头,并以显式或隐式的语句终止符结束。语句的示例包括:

> 3;
> let x = 3;
> if () {}
> for () {}
> while () {}

10

JavaScript中的所有程序都由语句构成,并以分号结束,除了用于组合零个或多个语句的块语句。 语句只执行一些操作,但不产生任何值或输出,而表达式则返回某些值。当解释器看到表达式时,它会检索其值并用新值替换表达式。语句用于操作这些表达式。您可以在此处检查它是表达式还是语句:JavaScript解析器


好的链接,Eldiyar。 - Ivan Kleshnin

9

打开浏览器控制台(Ctrl + Shift + ICommand + Shift + I):

输入你不确定的代码。

如果浏览器在每行执行后返回undefined,那么它是语句(除了实际上作为结果返回undefined的表达式)。

如果浏览器在每行执行后返回任何值,而不是undefined,那么它是表达式。

Chrome浏览器控制台图片显示了表达式和语句之间的差异

浏览器控制台似乎对于输入的每行代码都使用console.log(statement)。有关更多信息,请参见此处

这个过程仅用于验证。

但是必须理解:

  1. 表达式return值,而语句则没有。

  2. 所有表达式都是语句,但反之不成立。你不能用语句代替表达式。


如果浏览器在每行执行后返回undefined,则这是一个语句 - 除了那些实际上作为结果返回undefined的表达式。 - Victor
@Victor 那不是已经提到了吗? - KJ Sudarshan
不对。你写道:“如果浏览器在每行执行后返回undefined,则它是一个语句。” 表达式可能会返回undefined作为结果(例如(function(){ return undefined })()),但根据你的回答,它应该被视为语句。 - Victor
@Victor 感谢您指出这一点。我已经更新了答案。 - KJ Sudarshan

5

我强烈推荐阅读Axel Rauschmayer博士的全面博客文章JavaScript中的表达式与语句,如上面@ZER0的答案所提到的。

但是我最喜欢的记忆方法是:

表达式:
e可以被设置为等于一个表达式E
..或者通过打印来表达。
语句:
..其他任何东西。


以下内容修改自Anders Kaseorg在Quora上的回答

语句是执行某些操作的完整代码行。

每个表达式都可以用作语句
(其效果是评估表达式,并忽略结果值)。

但是表达式是代码中计算为值的任何部分。

使用运算符将表达式“水平”组合成更大的表达式。
E具有水平的平坦顶部

大多数语句不能用作表达式。

语句只能通过写在另一个语句之后或使用块结构“垂直”组合。
S相对于其他语句是垂直的。


来自Ryan Lam在Quora上的帖子:

这是一个常规的经验法则:如果您可以打印它或将其分配给变量,则为表达式。如果不能,则为语句。

以下是一些表达式的示例:

2 + 2
3 * 7
1 + 2 + 3 * (8 ** 9) - sqrt(4.0)
min(2, 22)
max(3, 94)
round(81.5)
"foo"
"bar"
"foo" + "bar"
None
True
False
2
3
4.0

所有上述内容都可以打印出来或赋值给一个变量。
以下是一些语句的示例:
if CONDITION:
elif CONDITION:
else:
for VARIABLE in SEQUENCE:
while CONDITION:
try:
except EXCEPTION as e:
class MYCLASS:
def MYFUNCTION():
return SOMETHING
raise SOMETHING
with SOMETHING:

以上这些结构都不能被赋值给一个变量。它们是语法元素,有一定的作用,但本身没有任何内在的“价值”。换句话说,这些结构不会“计算”出任何值。例如,试图做以下任何一个操作都是荒谬的,根本行不通:

x = if CONDITION:
y = while CONDITION:
z = return 42
foo = for i in range(10):

1

简而言之,表达式是值,语句是行为。

让我给你展示一些简单的例子。

以下代码定义了一个 。它是一个由 let 关键字引导的 语句,叫做 let 语句。这里的 1 是一个 表达式

// statement
// |---------|
   let n = 1 ;
//        |-|
//        expression

这是由for关键字引导的for语句,它带有一些嵌套的表达式和语句。

//      expression(1)
//      |-----------|
   for (num in numArr) {                 //--+
                                         //  |
      sum += (num * 3); // statement(2)  //  |
//            |-----|                    //  |statement(1)
//            expression(2)              //  |
                                         //  |
   }                                     //--+

所以,一个“语句”是一种指令单元,告诉计算机“该做什么”。 “for语句”,“while语句”,“if语句”,它们都是“操作”,换句话说,“任务”或“行为”。
但是对于“表达式”,我们谈论的是值、贵重物品、对象或某些可以产生值的过程,比如函数。
1 + 2
(2 + 3) * 5 === 25
new Foo()

所以JavaScript也有“函数表达式”。因为“函数”是一个“值”。在以下代码中,
  1. 整个片段是一个语句。
  2. 等号后的所有内容都是表达式。
  3. “return width * height;”是嵌套语句。
  4. “width * height”是一个表达式。
const rectArea = function(width, height) {
  return width * height;
};

0

产生一个值的代码片段被称为表达式。每个字面上写的值(例如22或“精神分析”)都是表达式。括号中的表达式也是表达式,两个表达式应用二元运算符或一个表达式应用一元运算符也是表达式。

表达式对应于人类语言中的句子片段,JavaScript语句对应于完整的句子。程序是语句列表。

最简单的语句类型是带有分号的表达式。这是一个程序:

1; !false; 虽然这是一个无用的程序。表达式可以只产生一个值,然后由封闭代码使用。语句独立存在,因此仅在影响世界时才起作用。

语句可能具有副作用。

https://eloquentjavascript.net/02_program_structure.html


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