本文来自公众号 猩猩程序员 欢迎关注
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 的模式匹配提案为我们描绘了一个美好的未来:
- 声明式:代码直接描述"你想要什么样的数据",而不是"你如何去检查数据"。
- 更安全 :它避免了
switch
语句忘记写break
导致的"意外贯穿"问题。 - 解构与匹配合一:在检查数据结构的同时完成解构赋值,代码更简洁。
- 处理复杂嵌套数据:对于复杂的 JSON 对象或 API 响应,模式匹配可以极大地简化处理逻辑。
虽然我们还需要等待一段时间才能在项目中使用它,但了解其设计思想有助于我们写出更清晰、更易于维护的条件逻辑。
学过Rust的人应该对这些语法比较熟悉,可以说,JavaScript 的模式匹配提案是 Rust match 表达式在动态语言领域的一次"精神传承"。它借鉴了 Rust 成熟的、富有表现力的语法,旨在解决传统 if/else 和 switch 在处理复杂数据结构时的笨拙和繁琐。
本文来自公众号 猩猩程序员 欢迎关注