Ramda工具的使用
Ramda是什么?
类似于lodash的JavaScript函数式编程库,如果你习惯于函数式编程pipeline,那么Ramda会非常适合你。
Ramda和lodash的区别
在目前的情况下,lodash的使用率更高,Ramda和lodash相比又有什么优势呢?
- Ramda 强调更加纯粹的函数式风格。数据不变性和函数无副作用是其核心设计理念。这可以帮助你使用简洁、优雅的代码来完成工作。
- Ramda 函数本身都是自动柯里化的。这可以让你在只提供部分参数的情况下,轻松地在已有函数的基础上创建新函数。
- Ramda 函数参数的排列顺序更便于柯里化。要操作的数据通常在最后面。
最后两点一起,使得将多个函数构建为简单的函数序列变得非常容易,每个函数对数据进行变换并将结果传递给下一个函数。Ramda 的设计能很好地支持这种风格的编程。
Ramda 还有一个特点:所有方法都支持柯里化。也就是说,所有多参数的函数,默认都可以单参数使用。
但是Ramda的性能相比较lodash来说仍然是要差一些的。
Ramda的使用方法
在react中的使用方法:
npm install ramda
使用babel-plugin-ramda减少打包体积
npm install babel-plugin-ramda --save
// 使用时在config中添加此plugin
在需要使用的地方导入:
import * as R from ramda
Ramda的常用方法
目前使用Ramda常常结合hooks一起使用
-
R.pipe
从左往右执行函数组合。第一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。
在一些库中,此函数也被称为
sequence。注意:
pipe函数的结果不是自动柯里化的const f = R.pipe(Math.pow, R.negate, R.inc); f(3, 4); // -(3^4) + 1 -
R.compose
从右往左执行函数组合(右侧函数的输出作为左侧函数的输入)。最后一个函数可以是任意元函数(参数个数不限),其余函数必须是一元函数。
**注意:**compose 输出的函数不会自动进行柯里化。
const classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName const yellGreeting = R.compose(R.toUpper, classyGreeting); yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND" R.compose(Math.abs, R.add(1), R.multiply(2))(-4) //=> 7 -
R.identity
占位符,将输入值原样返回,适用于默认或占位函数。
结合useSelector使用:
const { loading } = useSelector(R.identity) -
R.pick
返回对象的部分拷贝,其中仅包含指定键对应的属性。如果某个键不存在,则忽略该属性。
const { data } = useSelector(R.pick['myStore']) -
R.last
返回列表或字符串的最后一个元素。
R.last(['a', 'b', 'c']) -
R.prop
取出
对象中指定属性的值。如果不存在,则返回 undefined。
R.prop('x', {x: 100}); //=> 100 R.prop('x', {}); //=> undefined R.prop(0, [100]); //=> 100 R.compose(R.inc, R.prop('x'))({ x: 3 }) //=> 4 -
R.props
返回
prop的数组:输入为 keys 数组,输出为对应的 values 数组。values 数组的顺序与 keys 的相同。R.props(['x', 'y'], {x: 1, y: 2}); //=> [1, 2] R.props(['c', 'a', 'b'], {b: 2, a: 1}); //=> [undefined, 1, 2] const fullName = R.compose(R.join(' '), R.props(['first', 'last'])); fullName({last: 'Bullet-Tooth', age: 33, first: 'Tony'}); //=> 'Tony Bullet-Tooth' -
R.uniq
列表去重操作。返回无重复元素的列表。通过
R.equals函数进行相等性判断。R.uniq([1, 1, 2, 1]); //=> [1, 2] R.uniq([1, '1']); //=> [1, '1'] R.uniq([[42], [42]]); //=> [[42]] -
R.type和R.is
R.type用一个单词来描述输入值的(原生)类型,返回诸如 ‘Object’、‘Number’、‘Array’、‘Null’ 之类的结果。不区分用户自定义的类型,统一返回 ‘Object’。
R.is检测一个对象(
val)是否是给定构造函数的实例。该函数会依次检测其原型链,如果存在的话。R.type({}); //=> "Object" R.type(1); //=> "Number" R.type(false); //=> "Boolean" R.type('s'); //=> "String" R.type(null); //=> "Null" R.type([]); //=> "Array" R.type(/[A-z]/); //=> "RegExp" R.type(() => {}); //=> "Function" R.type(undefined); //=> "Undefined"R.is(Object, {}); //=> true R.is(Number, 1); //=> true R.is(Object, 1); //=> false R.is(String, 's'); //=> true R.is(String, new String('')); //=> true R.is(Object, new String('')); //=> true R.is(Object, 's'); //=> false R.is(Number, {}); //=> false -
R.equals
如果传入的参数相等,返回
true;否则返回false。可以处理几乎所有 JavaScript 支持的数据结构。若两个参数自身存在
equals方法,则对称地调用自身的equals方法。R.equals(1, 1); //=> true R.equals(1, '1'); //=> false R.equals([1, 2, 3], [1, 2, 3]); //=> true const a = {}; a.v = a; const b = {}; b.v = b; R.equals(a, b); //=> true -
R.path
取出给定路径上的值。
R.path(['a', 'b'], {a: {b: 2}}); //=> 2 R.path(['a', 'b'], {c: {b: 2}}); //=> undefined R.path(['a', 'b', 0], {a: {b: [1, 2, 3]}}); //=> 1 R.path(['a', 'b', -2], {a: {b: [1, 2, 3]}}); //=> 2 -
R.without
求第二个列表中,未包含在第一个列表中的任一元素的集合。通过
R.equals函数进行相等性判断。若在列表位置中给出 transfomer,则用作 transducer 。
R.without([1, 2], [1, 2, 1, 3, 4]); //=> [3, 4]
更多的方法可以参考remda官网进行查看,取值的方法加上Eq都可以进行判断比对
Pointfree示例
var str = 'Lorem ipsum dolor sit amet consectetur adipiscing elit';
上面是一个字符串,请问其中最长的单词有多少个字符?
我们先定义一些基本运算。
// 以空格分割单词
var splitBySpace = s => s.split(' ');
// 每个单词的长度
var getLength = w => w.length;
// 词的数组转换成长度的数组
var getLengthArr = arr => R.map(getLength, arr);
// 返回较大的数字
var getBiggerNumber = (a, b) => a > b ? a : b;
// 返回最大的一个数字
var findBiggestNumber =
arr => R.reduce(getBiggerNumber, 0, arr);
然后,把基本运算合成为一个函数(查看完整代码)
var getLongestWordLength = R.pipe(
splitBySpace,
getLengthArr,
findBiggestNumber
);
getLongestWordLength(str) // 11
可以看到,整个运算由三个步骤构成,每个步骤都有语义化的名称,非常的清晰。这就是 Pointfree 风格的优势。
Ramda 提供了很多现成的方法,可以直接使用这些方法,省得自己定义一些常用函数(查看完整代码)。
// 上面代码的另一种写法
var getLongestWordLength = R.pipe(
R.split(' '),
R.map(R.length),
R.reduce(R.max, 0)
);
Pointfree 示例二
拷贝自 Scott Sauyet 的文章《Favoring Curry》。那篇文章能帮助你深入理解柯里化,强烈推荐阅读。
下面是一段服务器返回的 JSON 数据。

现在要求是,找到用户 Scott 的所有未完成任务,并按到期日期升序排列。

过程式编程的代码如下(查看完整代码)。

上面代码不易读,出错的可能性很大。
现在使用 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')));
};
上面代码已经清晰很多了。
另一种写法是,把各个then里面的函数合成起来(查看完整代码)。
// 提取 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,
)
);
};
上面的代码跟过程式的写法一比较,孰优孰劣一目了然。
小结
ramda最大的特点就是结合函数式编程,利用pipe或者compose来组合函数,类似管道的方式进行数据流动。
Ramda是一款强调函数式编程风格的JavaScript库,支持数据不可变性和函数无副作用,适合构建简洁优雅的应用。本文介绍Ramda的基本概念、与lodash的区别及在React中的应用。
154

被折叠的 条评论
为什么被折叠?



