JavaScript的 表达式 vs. 语句

153 阅读6分钟

表达式(Expression)

定义: 表达式是会计算并返回一个值的代码片段

特点:

  • 表达式总是有返回值
  • 表达式可以出现在任何需要值的地方,可以出现在赋值语句、函数参数、控制流语句的条件部分等需要值的地方,例如 let x = (1 + 2)

示例:

42; // 数字表达式,返回值是 42
"Hello"; // 字符串表达式,返回值是 "Hello"
1 + 2; // 算术表达式,返回值是 3
console.log("Big number") // 函数是表达式,此函数返回值: undefined

语句(Statement)

定义: 语句是用于执行某种操作的代码块,包括控制流、声明、循环等。例如:if (...) {}for (...) {}let x = 5;return 42;

特点:

  • 语句通常不返回值,或者返回值不被直接使用。语句一般由多个部分构成,执行一个操作。
  • 语句的内容可以包含表达式,表达式本身可以作为语句的一部分。

示例:

let a = 1; // 声明语句
if (true) {} // 条件语句
for (let i = 0; i < 10; i++) {} // 循环语句

表达式和语句的关系

关系: 表达式和语句并不是一个维度,更像是**“内容 vs. 结构”**的关系,而不是两种并列的类型。

  • 表达式 = 计算并返回一个值
  • 语句 = 执行某个操作,是代码的一个组成部分,可能包含多个表达式,但通常没有返回值

语句可以包含表达式,但表达式本身不是语句的一种,而是语句的组成部分

示例:

let x = 1 + 2;
  • 1 + 2 是表达式(计算并返回 3)
  • x = 1 + 2 是表达式,赋值表达式
  • let x = 1 + 2 是语句(执行赋值操作)
if (x > 10) { console.log("Big number"); }
  • x > 10 是表达式(计算出 true 或 false)
  • console.log("Big number") 是表达式,只是返回 undefined
  • if (...) {} 是语句(执行分支逻辑)

表达式语句(Expression Statement)

定义: 当一个表达式独立成行时,它会被视为表达式语句。它会执行表达式并丢弃其返回值

示例:

"Hello";      // 表达式语句
1 + 2;        // 表达式语句
console.log("Hi"); // 也是表达式语句(函数调用也是表达式)

本质上,表达式语句仍然是语句,但它的内容是一个表达式。

特殊的表达式

某些语句单独成行使用时,不是表达式语句,但在某些情况下使用会被视为表达式

函数声明和类声明

function fun() {}
class A {}

1. 函数声明和类声明单独成行使用

它们只是声明语句,不是表达式语句,它的作用是创建函数或类的绑定,不会直接返回值;

2. 用括号括起来,并在单独成行的情况下是表达式语句

(function() {}); // 函数表达式语句,也可以是具名函数
(class {});      // 类表达式语句,也可以是具名类

// 匿名函数和匿名类 不被括号包裹,单独成行时,会报语法错误

3. 做为函数参数使用时,是表达式

console.log(function () {}); //  函数表达式
console.log(class {});       //  类表达式

4. 赋值语句中使用时,是表达式

let foo = function() {} // 函数也可以是具名函数
let MyClass = class {}; // 类也可以是具名类

function() {} 此时是一个表达式,因为他返回了一个函数,并赋值给了foo

class {} 此时是一个表达式,因为他返回了一个类,并赋值给了MyClass

语法上,它们是声明赋值语句的一部分,包含了 函数表达式类表达式。因为它们的主要功能是定义函数或类,大家为了简化和突出它们的作用,整个语句通常称其为 函数表达式 或 类表达式

5. 默认导出语句中使用时,是表达式

export default function() {}
export default class {}

function() {} 此时是一个表达式,因为他返回了一个函数,并被默认导出

class {} 此时是一个表达式,因为他返回了一个类,并被默认导出

6. 注意

  • 函数声明有提升效果,可以在定义前调用。
  • 函数表达式没有提升效果,必须在定义后调用。
  • 函数表达式如果没有赋值给变量,外部无法访问(即使是具名函数)。
  • 立即执行函数表达式(IIFE):
    • 匿名 IIFE 无法通过函数名递归调用自身。
    • 具名 IIFE 可以通过函数名递归调用自身。
    • 非严格模式下,匿名函数可以通过 arguments.callee 访问自身。
  • 类表达式如果没有赋值给变量,外部无法访问(即使是具名类)。

import 语句

import a from './module.js';
let module = import('./module.js'); 

import 语句本质上是 模块导入的声明,不能作为表达式使用,也不会返回值。

import() 执行调用时,会返回一个 Promise 对象,代表异步加载模块的结果。是表达式

总结

  • 表达式是计算值并返回结果的代码片段。表达式可以作为语句的一部分,但在独立使用时,称为表达式语句,执行计算并丢弃返回值。
  • 语句是执行某个操作的代码块,它不直接返回一个值,语句可能包含多个表达式。
  • 函数声明类声明单独使用时是声明语句,但它们在一个语句中作为值使用时是表达式,返回一个函数或类。
  • 函数作为表达式运行时,将没有函数提升效果,且如果没有赋值给其它变量,外部将不能访问此函数。
  • import 语句是声明,不是表达式,而 import() 作为表达式使用时返回一个 Promise

JavaScript 中的表达式常见分类

分类说明示例
1. 原始表达式(Primary Expressions)直接返回值的最基础表达式42, "Hello", true, null, undefined, this, a
2. 算术表达式计算数值的表达式1 + 2, -a, ++a
3. 逻辑表达式计算布尔值的表达式true && false, !true, `a
4. 比较表达式用于比较两个值a === b, a > b
5. 赋值表达式给变量赋值的表达式a = 5, a += 2
6. 条件(三元)表达式if语句的简写形式a > b ? a : b
7. 函数表达式必须用 ()包裹或赋值(function() {}), const foo = function() {};
8. 类表达式必须用 ()包裹或赋值(class {}), const MyClass = class {};
9. 对象和数组表达式直接创建对象或数组{ name: "Alice" }, [1, 2, 3]
10. 模板字符串表达式通过模板字符串拼接`Hello, ${name}!`
11. 可调用表达式(调用和 new)调用函数或构造实例foo();, obj.method();, new Date();
12. 成员访问表达式访问对象或数组的成员obj.name, arr[0]
13. 分组表达式控制运算优先级(不影响类型)(1 + 2) * 3
14. 动态导入表达式动态加载模块的表达式import('./module.js')