JavaScript函数式编程之pointfree与声明式编程

简介: 更多相关内容见博客 github.com/zhuanyongxi… 函数式编程中的pointfree的意思就是“无参”或“无值”,pointfree style是一种编程范式,也作tacit programming,就是“无参编程”的意思了。

更多相关内容见博客 github.com/zhuanyongxi…

函数式编程中的pointfree的意思就是“无参”或“无值”,pointfree style是一种编程范式,也作tacit programming,就是“无参编程”的意思了。什么是“无参编程”?


// 这就是有参的,因为有word
var snakeCase = word => word.toLowerCase().replace(/\s+/ig, '_');

// 这是pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);

从另一个角度看,有参的函数的目的是得到一个数据,而pointfree的函数的目的是得到另一个函数。 所以,如下的方程,虽然也有参,也可以认为是pointfree的。


const titlesForYear = year =>
  pipe(
    filter(publishedInYear(year)),
    map(book => book.title)
  )

那这pointfree有什么用? 它可以让我们把注意力集中在函数上,参数命名的麻烦肯定是省了,代码也更简洁优雅。 需要注意的是,一个pointfree的函数可能是由众多非pointfree的函数组成的,也就是说底层的基础函数大都是有参的,pointfree体现在用基础函数组合而成的高级函数上。如果我们使用函数式编程的工具,如ramda,这些基础函数大都已经被写好了,这样我们去写pointfree的代码就很容易了。

什么是声明式编程?它区别于命令式编程


// 命令式
var words = [];
for (i = 0; i < otherWords.length; i++) {
  words.push(otherWords[i].word);
}

// 声明式
var words = otherWords.map(function(ele){ return ele.word; });

容易看出,命令式的代码,我们不但要去遍历,还要关注如何遍历。而声明式的就容易很多,可以节省我们的注意力,代码也更加简洁。

其他的命令式的写法有:使用ifelse进行的条件判断,使用算数运算符进行的算数运算,使用比较运算符进行的比较运算和使用逻辑运算符进行的逻辑运算。

至于那些说“虽然如此,但使用命令式循环速度要快很多”的人,我建议你们先去学学 JIT 优化代码的相关知识。这里有一个非常棒的视频,可能会对你有帮助。

需要注意的是,要实现这种声明式的编程,首先我们要有这个map方法,这一点与pointfree相同,都是需要我们先对常用的操作做一次封装,而这些常用的操作本身还是命令式的。

pointfree的声明式代码是函数式编程应该有的样子。

最后用一个来自Scott Sauyet的文章《Favoring Curry》中的例子,使用的函数式工具是ramda。下面的代码不需要一句一句的看,大概体会一下就可以了。

一组JSON数据


var data = {
    result: "SUCCESS",
    interfaceVersion: "1.0.3",
    requested: "10/17/2013 15:31:20",
    lastUpdated: "10/16/2013 10:52:39",
    tasks: [
        {id: 104, complete: false,            priority: "high",
                  dueDate: "2013-11-29",      username: "Scott",
                  title: "Do something",      created: "9/22/2013"},
        {id: 105, complete: false,            priority: "medium",
                  dueDate: "2013-11-22",      username: "Lena",
                  title: "Do something else", created: "9/22/2013"},
        {id: 107, complete: true,             priority: "high",
                  dueDate: "2013-11-22",      username: "Mike",
                  title: "Fix the foo",       created: "9/22/2013"},
        {id: 108, complete: false,            priority: "low",
                  dueDate: "2013-11-15",      username: "Punam",
                  title: "Adjust the bar",    created: "9/25/2013"},
        {id: 110, complete: false,            priority: "medium",
                  dueDate: "2013-11-15",      username: "Scott",
                  title: "Rename everything", created: "10/2/2013"},
        {id: 112, complete: true,             priority: "high",
                  dueDate: "2013-11-27",      username: "Lena",
                  title: "Alter all quuxes",  created: "10/5/2013"}
        // , ...
    ]
};

需求是找到Scott所有未完成的任务,并按照到期日期升序排列。

正确的结果是

[
    {id: 110, title: "Rename everything", 
        dueDate: "2013-11-15", priority: "medium"},
    {id: 104, title: "Do something", 
        dueDate: "2013-11-29", priority: "high"}
]

命令式的代码如下


getIncompleteTaskSummaries = function(membername) {
    return fetchData()
        .then(function(data) {
            return data.tasks;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (tasks[i].username == membername) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (!tasks[i].complete) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [], task;
            for (var i = 0, len = tasks.length; i < len; i++) {
                task = tasks[i];
                results.push({
                    id: task.id,
                    dueDate: task.dueDate,
                    title: task.title,
                    priority: task.priority
                })
            }
            return results;
        })
        .then(function(tasks) {
            tasks.sort(function(first, second) {
                var a = first.dueDate, b = second.dueDate;
                return a < b ? -1 : a > b ? 1 : 0;
            });
            return tasks;
        });
};

pointfree的代码


var getIncompleteTaskSummaries = function(membername) {
  return fetchData()
    .then(R.prop('tasks'))
    .then(R.filter(R.propEq('username', membername)))
    .then(R.reject(R.propEq('complete', true)))
    .then(R.map(R.pick(['id', 'dueDate', 'title', 'priority'])))
    .then(R.sortBy(R.prop('dueDate')));
};

pointfree的声明式的代码


// 提取 tasks 属性
var SelectTasks = R.prop('tasks');

// 过滤出指定的用户
var filterMember = member => R.filter(
  R.propEq('username', member)
);

// 排除已经完成的任务
var excludeCompletedTasks = R.reject(R.propEq('complete', true));

// 选取指定属性
var selectFields = R.map(
  R.pick(['id', 'dueDate', 'title', 'priority'])
);

// 按照到期日期排序
var sortByDueDate = R.sortBy(R.prop('dueDate'));

// 合成函数
var getIncompleteTaskSummaries = function(membername) {
  return fetchData().then(
    R.pipe(
      SelectTasks,
      filterMember(membername),
      excludeCompletedTasks,
      selectFields,
      sortByDueDate,
    )
  );
};


原文发布时间为:2018年06月17日
原文作者:砖用冰西瓜

本文来源: 掘金 如需转载请联系原作者




相关文章
|
2月前
|
存储 JavaScript 前端开发
JavaScript编程实现tab选项卡切换的效果+1
JavaScript编程实现tab选项卡切换的效果+1
|
3月前
|
JavaScript 前端开发 编译器
解锁JavaScript模块化编程新纪元:从CommonJS的基石到ES Modules的飞跃,探索代码组织的艺术与科学
【8月更文挑战第27天】随着Web应用复杂度的提升,JavaScript模块化编程变得至关重要,它能有效降低代码耦合度并提高项目可维护性及扩展性。从CommonJS到ES Modules,模块化标准经历了显著的发展。CommonJS最初专为服务器端设计,通过`require()`同步加载模块。而ES Modules作为官方标准,支持异步加载,更适合浏览器环境,并且能够进行静态分析以优化性能。这两种标准各有特色,但ES Modules凭借其更广泛的跨平台兼容性和现代语法逐渐成为主流。这一演进不仅标志着JavaScript模块化的成熟,也反映了整个JavaScript生态系统的不断完善。
48 3
|
1天前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
9 2
|
2月前
|
JavaScript 前端开发
JavaScript编程实现tab选项卡切换的效果
JavaScript编程实现tab选项卡切换的效果
|
2月前
|
JavaScript 前端开发
用JavaScript编程控制网页上checkbox选择状态:全选、全部取消、反选
用JavaScript编程控制网页上checkbox选择状态:全选、全部取消、反选
|
2月前
|
JavaScript 前端开发 安全
JavaScript编程实现字符和字符串翻转
JavaScript编程实现字符和字符串翻转
|
2月前
|
JavaScript 前端开发
用JavaScript编程定义二维数组并初始化,然后输出元素值
用JavaScript编程定义二维数组并初始化,然后输出元素值
|
3月前
|
JavaScript 前端开发 安全
揭秘TypeScript的魔力:它是如何华丽变身为JavaScript的超能英雄,让您的代码飞入全新的编程维度!
【8月更文挑战第22天】在Web开发领域,JavaScript是最主流的编程语言之一。但随着应用规模的增长,其类型安全和模块化的不足逐渐显现。为解决这些问题,微软推出了TypeScript,这是JavaScript的一个超集,通过添加静态类型检查来提升开发效率。TypeScript兼容所有JavaScript代码,并引入类型注解功能。
36 2
|
3月前
|
JavaScript 前端开发 Oracle
|
3月前
|
JavaScript 前端开发 开发者
震撼揭秘!JS模块化进化史:从混沌到秩序,一场代码世界的华丽蜕变,你怎能错过这场编程盛宴?
【8月更文挑战第23天】在 Web 前端开发领域,JavaScript 模块化已成为处理日益复杂的 Web 应用程序的关键技术。通过将代码分解成独立且可重用的模块,开发者能够更有效地组织和管理代码,避免命名冲突和依赖混乱。从最早的全局函数模式到 IIFE,再到 CommonJS 和 AMD,最终进化到了 ES6 的原生模块支持以及 UMD 的跨环境兼容性。本文通过具体示例介绍了这些模块化规范的发展历程及其在实际开发中的应用。
45 0