Please enable Javascript to view the contents

JavaScript 模式匹配初探

 ·  ☕ 3 分钟

什么是模式匹配

模式匹配(pattern matching )被一众函数式语言(Rust, F#, Scala,Elixir,Erlang)广泛采用。

模式匹配是一种“分发机制”,泛指各语言中用作"动态地选择行为"的特性。

具体而言,模式匹配是条件分支语句的一种形式,是更简洁、抽象化的if else/switch case 语句。

下面一段JavaScript中switch case 语句:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function print_color(color) {
  switch (color) {
    case "rose":
      console.log("roses are red,");
      break;
    case "violet":
      console.log("violets are blue,");
      break;
    default:
      console.log("sugar is sweet, and so are you.");
  }
}

print_color("rose"); // roses are red,
print_color("violet"); // violets are blue,
print_color("you"); // sugar is sweet, and so are you.

与之等效的rust 模式匹配实现代码为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fn print_color(color: &str) {
  match color {
    "rose" => println!("roses are red,"),
    "violet" => println!("violets are blue,"),
    _ => println!("sugar is sweet, and so are you."),
  }
}

fn main() {
  print_color("rose"); // roses are red,
  print_color("violet"); // violets are blue,
  print_color("you"); // sugar is sweet, and so are you.
}

从以上JavaScript与Rust代码比较来看,rust模式匹配代码确实要简单一些。

但其实javascript程序员一直在使用类似模式匹配的方式做条件判断,这就是对象字面量,代码如下:

function print_color(color) {
  const flowers = {
    'rose':"roses are red,",
    'violet':"violets are blue,",
    'default':"sugar is sweet, and so are you."
  }
  console.log( flowers[color] ? flowers[color] : flowers['default'])
}

print_color("rose"); // roses are red,
print_color("violet"); // violets are blue,
print_color("you"); // sugar is sweet, and so are you.

这是JavaScript程序员集体自发的操作,现在ECMAScrip官方正计划把该功能标准化。


JavaScript中的模式匹配

tc39/proposal-pattern-matching: Pattern matching syntax for ECMAScript

ECMAScript在基于解构赋值的基础上,为JavaScript添加模式匹配的特性。

目前该提案已在2018年5月的TC39会议上被初步通过,目前处于Stage 1阶段。

一个ECMAScript标准的制作过程,包含了Stage 0Stage 45个阶段,每个阶段提交至下一阶段都需要TC39审批通过。各个阶段介绍如下:

  1. Stage 0Strawman阶段)- 该阶段是一个开放提交阶段,任何在TC39注册过的贡献者或TC39成员都可以进行提交。
  2. Stage 1Proposal阶段)- 该阶段是对所提交新特性的正式建议。
  3. Stage 2Draft阶段)- 该阶段是会出现标准中的第一个版本。
  4. Stage 3Canidate阶段)- 该阶段的提议已接近完成,只需要得到提议实现方的反馈,并由用户来进一步推动。
  5. Stage 4Finished阶段)- 该阶段的会被包括到标准之中。

虽然已经到了Stage 1的阶段,但是进化的路程是比较坎坷的,各种语法层面上的纷争从来没有停过。比如:

  • 关键字(match…with 还是case…when)
  • 符号(->/~>)
  • 是否支持 else clause(可以 match 所有情况的 clause)

当然最终最变成什么样子还是由 tc39 说得算。从tc39给出的示例代码来看:

代码片段1,匹配 fetch()的响应:

const res = await fetch(jsonService)
case (res) {
  when {status: 200, headers: {'Content-Length': s}} ->
    console.log(`size is ${s}`),
  when {status: 404} ->
    console.log('JSON not found'),
  when {status} if (status >= 400) -> {
    throw new RequestError(res)
  },
}

代码片段2:

const getLength = vector => case (vector) {
  when { x, y, z } -> Math.hypot(x, y, z)
  when { x, y } -> Math.hypot(x, y)
  when [...etc] -> vector.length
}
getLength({x: 1, y: 2, z: 3}) // 3.74165

目前JavaScript模式匹配的语法应该是:

  1. case when
  2. -> 瘦箭头
  3.  支持if else
  4. 自动解构赋值

JavaScript 模式匹配实现

虽然官方目前还没有出台模式匹配标准api,但是一些js类库已经实现模式匹配功能,如:

以 Z 为例子:

  • 安装: npm install z
  • 从Z中引入matches函数: const { matches } = require('z')

实例:匹配对象属性

1
2
3
4
5
6
7
8
9
const { matches } = require('z')

const person = { name: 'Maria' }
matches(person)(
  (x = { name: 'John' }) => console.log('John you are not welcome!'),
  (x)                    => console.log(`Hey ${x.name}, you are welcome!`)
)

//output: `Hey Maria, you are welcome!`

参考资料

分享

码中人
作者
码中人
Web Developer