前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PointFree编程风格

PointFree编程风格

作者头像
kifuan
发布2022-10-24 16:28:45
3650
发布2022-10-24 16:28:45
举报
文章被收录于专栏:随便写写-kifuan

源码

点击这里前往Github获取本文源码。

背景

在阅读完阮一峰老师的文章Pointfree JavaScript后,突然感觉自己对函数式编程的理解提升了,所以写下这篇文章。

这里的Point指的是函数的实参,所以PointFree就是没有实参的情况下进行函数组合的编程风格。

常规形式

例子来源于Pointfree JavaScript

我们来试着分析一下这段代码干了什么:

代码语言:javascript
复制
function getAdminEmails(users) {
    const emails = []
    for (const user of users) {
        if (user.role === 'admin') {
            emails.push(user.email)
        }
    }
    return emails
}
  1. 定义了一个emails数组。
  2. 遍历users数组。
  3. 检查user对象的role属性是否是admin,如果是,就把他的email属性添加到emails数组中。
  4. 返回emails数组。

但如果你细心的话,会发现这个函数名已经说明了一切,它就是获取管理员的邮箱而已。

下面是测试代码,读者可以打开控制台运行:

代码语言:javascript
复制
const allUsers = [
    { role: 'admin', email: 'a@b.com' }, 
    { role: 'user', email: 'c@d.com' },
    { role: 'admin', email: 'e@f.com' }
]

console.log(getAdminEmails(allUsers))
// [ 'a@b.com', 'e@f.com' ]

所以说我们会发现用传统形式编程固然可以达到结果,但是还不是最优解,所以我们试着用一下数组提供的一些函数式编程API。

数组的函数式API

改写一下上面的函数,可以这样:

代码语言:javascript
复制
function getAdminEmails(users) {
    return users.filter(user => user.role === 'admin')
        .map(user => user.email)
}

现在代码十分清晰了,我们就是把role属性为admin的筛选出来,然后获取它们的email属性。

接下来就是这篇文章要说的中心了。

PointFree风格

所谓PointFree风格,就是把功能拆分成非常小的几个点,之后再组合起来,在一切函数调用之前,我们都不需要关心实参是什么,只需要关注自己的逻辑即可。

既然说到组合,不妨先定义一个函数,用来组合一堆函数:

代码语言:javascript
复制
function compose(f, g) {
    return x => f(g(x))
}

和数学里接触到的挺像,那么我们接下来要做的事就是考虑getAdminEmails需要哪些组成部分:

  1. 我们要获取邮箱,获取谁的邮箱呢?
  2. 我们需要获取管理员的邮箱。

所以说写下来就是这样的一种形式:

代码语言:javascript
复制
const getAdminEmails = compose(
    getEmailsOf,
    adminUsers
)

那么就可以通过实现这两个函数来组合成我们想要的结果了,先定义getEmailOf

代码语言:javascript
复制
function getEmailsOf(users) {
    return users.map(user => user.email)
}

虽然这样写是可以的,但是所谓PointFree风格是想让我们所有的操作都通过一个个函数完成,所以说下面的代码符合风格:

代码语言:javascript
复制
const prop = key => obj => obj[key]

const map = mapper => array => array.map(mapper)

const getEmailsOf = map(prop('email'))

一开始上来可能有些难理解,但是理解之后就会知道其中的精妙之处了:

  1. prop(key)会返回获取某个对象的key属性的函数。
  2. map(mapper)会返回用mapper映射某个数组的函数。
  3. map(prop('email'))会返回用成员的email属性映射数组的函数。

仿照上面的例子,我们写出adminUsers函数:

代码语言:javascript
复制
const propIs = value => key => obj => prop(key)(obj) === value

const filter = predicate => array => array.filter(predicate)

const adminUsers = filter(propIs('admin')('role'))

综合起来,最后的代码应该是这样的:

代码语言:javascript
复制
const compose = (f, g) => x => f(g(x))

const prop = key => obj => obj[key]

const propIs = value => key => obj => prop(key)(obj) === value

const map = mapper => array => array.map(mapper)

const filter = predicate => array => array.filter(predicate)

const getAdminEmails = compose(
    map(prop('email')),
    filter(propIs('admin')('role'))
)

const allUsers = [
    { role: 'admin', email: 'a@b.com' }, 
    { role: 'user', email: 'c@d.com' },
    { role: 'admin', email: 'e@f.com' }
]

console.log(getAdminEmails(allUsers))
// [ 'a@b.com', 'e@f.com' ]

读者可以试着到控制台运行一下,查看最终的结果。

我认为这种风格难理解的原因就是它箭头函数用的太多了,让人一下反应不过来,但是细想会觉得这种编程是非常巧妙的,因为最终的函数由一个个小函数组合而成,那么逻辑有问题的时候就可以一个个单独测试这些小函数有没有问题,使得debug更容易。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 源码
  • 背景
  • 常规形式
  • 数组的函数式API
  • PointFree风格
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档