什么是模式匹配

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

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

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

下面一段JavaScript中switch case 语句:

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 模式匹配实现代码为:

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 – Pattern Matching for Javascript

  • Babel Plugin

  • Sweet.js macro

以 Z 为例子:

  • 安装: npm install z

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

实例:匹配对象属性

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!`