什么?JavaScript 也搞了个类似 Rust 的模式匹配语法

本文来自公众号 猩猩程序员 欢迎关注

Rust 的 match 是其语言的基石之一,以其强大的表现力和编译时安全检查而闻名。JS 提案几乎是将其核心思想"翻译"到了 JavaScript 的动态世界中。

我们来深入解读一下 TC39 关于 JavaScript 模式匹配(Pattern Matching)提案的核心内容。

请注意,这个功能目前仍处于 Stage 1 提案阶段 ,意味着它正在早期探索中,语法和行为将来可能会有很大变化,现在还不能在任何浏览器或 Node.js 环境中直接使用。


JavaScript 模式匹配教程 (基于 TC39 提案)

想象一下,如果 if/else 和 switch 语句能变得更强大、更具表现力,能够直接解构并检查对象的"形状",那会怎么样?这就是模式匹配想要解决的问题。它旨在提供一种更声明式、更强大的方式来根据数据的结构执行不同的代码逻辑。

1. 核心语法:match 表达式

模式匹配的核心是一个新的表达式:match。记住,它是一个表达式 ,不是一个语句,这意味着它会返回一个值。

基本结构如下:

javascript 复制代码
// 这是一个尚未实现的语法
let result = match (someValue) {
  when (pattern1) { /* ... do something ... */ }
  when (pattern2 if someCondition) { /* ... do something else ... */ }
  // ... more when clauses ...
  else { /* ... default case ... */ }
}
  • match (someValue):将 someValue 作为输入进行匹配。
  • when (pattern):定义一个模式。someValue 会依次与每个 pattern 进行比较。
  • { ... }:一旦找到第一个匹配的 pattern,对应的代码块就会执行,并且 match 表达式会返回这个代码块的结果。
  • if someCondition:这是可选的"守卫子句 (Guard Clause)",允许你在模式匹配的基础上增加额外的判断条件。
  • else { ... }:如果没有任何 when 子句匹配成功,则执行 else 块。这类似于 switch 中的 default。

2. 强大的武器:模式 (Patterns)

这部分是模式匹配最激动人心的地方,也是提案的核心。一个"模式"可以是一个简单的值,也可以是一个复杂的结构。

a. 字面量模式 (Literal Pattern)

这是最简单的模式,用于匹配精确的值,就像 switch 的 case 一样。

javascript 复制代码
function getMimeType(fileExtension) {
  return match (fileExtension) {
    when ('html') { 'text/html' }
    when ('css') { 'text/css' }
    when ('js') { 'application/javascript' }
    else { 'application/octet-stream' }
  };
}

console.log(getMimeType('js')); // 输出: 'application/javascript'
b. 标识符模式 (Identifier Pattern) & 绑定

你可以使用一个变量名作为模式,这会匹配任何值,并将该值绑定到这个变量名上 ,以便在 when 的代码块和守卫子句中使用。

javascript 复制代码
function handleResponse(response) {
  return match (response) {
    // 匹配任何值,并将其绑定到 status 变量
    when ({ status } if status >= 200 && status < 300) {
      return `Success with status: ${status}`;
    }
    when ({ status }) { // 再次绑定
      return `Failure with status: ${status}`;
    }
  }
}
c. 通配符模式 (Wildcard Pattern _)

如果你想匹配任何东西,但又不关心 它的具体值(即不需要绑定它),可以使用下划线 _ 作为通配符。

在 else 块的内部,else 实际上就是 when (_) 的语法糖。

javascript 复制代码
function isImportant(message) {
  return match (message) {
    when ({ type: 'error' }) { true }
    when ({ type: 'warning' }) { true }
    // 匹配任何其他情况,但不关心其内容
    when (_) { false } 
  };
}
d. 对象模式 (Object Pattern)

这是模式匹配真正强大的地方。你可以匹配一个对象的"形状",并同时解构它。

javascript 复制代码
function handleAction(action) {
  return match (action) {
    // 匹配一个有 type: 'FETCH_SUCCESS' 和 data 属性的对象
    // 同时将 data 属性的值绑定到 payload 变量
    when ({ type: 'FETCH_SUCCESS', data: payload }) {
      console.log('Data received:', payload);
      return 'Displaying data.';
    }
    // 匹配一个有 type: 'FETCH_ERROR' 和 error 属性的对象
    // 同时将 error 属性的值绑定到 error 变量
    when ({ type: 'FETCH_ERROR', error }) { // { error } 是 { error: error } 的简写
      console.error('Error:', error.message);
      return 'Displaying error message.';
    }
    when ({ type: 'FETCH_PENDING' }) {
      return 'Loading...';
    }
  }
}

handleAction({ type: 'FETCH_SUCCESS', data: { user: 'Alice' } });
// 输出: Data received: { user: 'Alice' }
e. 数组模式 (Array Pattern)

与对象模式类似,你可以匹配数组的结构,包括固定元素、剩余元素等。

javascript 复制代码
function executeCommand(command) {
  return match (command) {
    // 匹配以 'add' 开头,且后面跟着一个元素的数组
    when (['add', item]) {
      console.log(`Adding ${item}...`);
    }
    // 匹配以 'delete' 开头,且后面跟着一个数字 ID 的数组
    when (['delete', id] if typeof id === 'number') {
      console.log(`Deleting item #${id}...`);
    }
    // 匹配一个空数组
    when ([]) {
      console.log('No command given.');
    }
    // 匹配任何其他数组,并将所有元素绑定到 `args`
    when ([...args]) {
      console.log(`Unknown command: ${args.join(' ')}`);
    }
  }
}

executeCommand(['delete', 123]); // 输出: Deleting item #123...
executeCommand(['move', 'file.txt']); // 输出: Unknown command: move file.txt

3. 点睛之笔:守卫子句 (Guard Clause if)

有时,仅匹配结构是不够的,你还需要检查值的属性。if 守卫子句让这一切成为可能。

javascript 复制代码
function getDiscount(user) {
  return match (user) {
    // 匹配一个 VIP 用户,并且他的消费超过 1000
    when ({ type: 'VIP', spending } if spending > 1000) {
      return 0.2; // 20% 折扣
    }
    // 匹配任何 VIP 用户
    when ({ type: 'VIP' }) {
      return 0.1; // 10% 折扣
    }
    // 匹配一个普通用户,并且是新注册的
    when ({ type: 'Normal', isNew: true }) {
      return 0.05; // 5% 折扣
    }
    else {
      return 0;
    }
  }
}

粽结

JavaScript 的模式匹配提案为我们描绘了一个美好的未来:

  1. 声明式:代码直接描述"你想要什么样的数据",而不是"你如何去检查数据"。
  2. 更安全 :它避免了 switch 语句忘记写 break 导致的"意外贯穿"问题。
  3. 解构与匹配合一:在检查数据结构的同时完成解构赋值,代码更简洁。
  4. 处理复杂嵌套数据:对于复杂的 JSON 对象或 API 响应,模式匹配可以极大地简化处理逻辑。

虽然我们还需要等待一段时间才能在项目中使用它,但了解其设计思想有助于我们写出更清晰、更易于维护的条件逻辑。

学过Rust的人应该对这些语法比较熟悉,可以说,JavaScript 的模式匹配提案是 Rust match 表达式在动态语言领域的一次"精神传承"。它借鉴了 Rust 成熟的、富有表现力的语法,旨在解决传统 if/else 和 switch 在处理复杂数据结构时的笨拙和繁琐。

本文来自公众号 猩猩程序员 欢迎关注

相关推荐
卸任23 分钟前
Vibe coding工具使用心得
前端·ai编程
brien91 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance1 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M1 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写2 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李2 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易2 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马2 小时前
前沿视界
前端
kyriewen2 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易2 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员