郭培

10个可以使用ES6替换的Lodash特性

郭培 · 2017-02-24翻译 · 1126阅读 原文链接

本文得到 Mark Brown的同行校审,非常感谢SitePoint的各位审稿人,是他们保证了SitePoint的内容质量可以做的更好。

到目前为止,Lodash成为必备的Npm包,但是如果你正在使用ES6,那么你就不一定要使用它。在本文中,我们将使用带箭头函数的本地集合方法和其他新的ES6功能,来帮忙我们实现许多流行的用例。

1. Map,Filter,Reduce

这些集合方法使数据操作变得轻而易举,并且这些方法得到了广泛的支持,我们可以使用这些方法配合箭头函数,来编写一些替代方案来替代Lodash提供的实现。

_.map([1, 2, 3], function(n) { return n * 3; });
// [3, 6, 9]
_.reduce([1, 2, 3], function(total, n) { return total + n; }, 0);
// 6
_.filter([1, 2, 3], function(n) { return n <= 2; });
// [1, 2]
// 替代成
[1, 2, 3].map(n => n * 3);
[1, 2, 3].reduce((total, n) => total + n);
[1, 2, 3].filter(n => n <= 2);

它能做的不止这些,如果我们使用ES6 polyfill,我们也可以使用find, some, everyreduceRight

2. Head & Tail

解构语法允许我们获取无效果函数返回列表的头部和尾部。

_.head([1, 2, 3]);
// 1
_.tail([1, 2, 3]);
// [2, 3]

// 替代成

const [head, ...tail] = [1, 2, 3];

我们还能以类似的方式获得初始元素(译者注:最后一个元素之前的元素)和最后一个元素。

_.initial([1, 2, 3]);
// -> [1, 2]
_.last([1, 2, 3]);
// 3

// 替代成

const [last, ...initial] = [1, 2, 3].reverse();

如果你觉得反向变化数据结构很烦人,你还可以在调用reverse之前使用spread操作符来克隆数组。

const xs = [1, 2, 3];
const [last, ...initial] = [...xs].reverse();

3. Rest & Spread

restspread函数允许我们定义和调用参数数量可变的函数。ES6引入这两种操作的专用语法。

var say = _.rest(function(what, names) {
  var last = _.last(names);
  var initial = _.initial(names);
  var finalSeparator = (_.size(names) > 1 ? ', & ' : '');
  return what + ' ' + initial.join(', ') +
    finalSeparator + _.last(names);
});

say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"

// 替换成

const say = (what, ...names) => {
  const [last, ...initial] = names.reverse();
  const finalSeparator = (names.length > 1 ? ', &' : '');
  return `${what} ${initial.join(', ')} ${finalSeparator} ${last}`;
};

say('hello', 'fred', 'barney', 'pebbles');
// "hello fred, barney, & pebbles"

4. Curry

没有像TypeScriptFlow这样的高级语言,我们就不能给函数类型签名,这样使得currying相当困难。当我们传入已curry过的函数时,很难知道已经提供了多少参数,下一步需要提供哪些参数。使用箭头函数,我们可以显式的定义已curry过的函数,使其他程序员更容易理解。

function add(a, b) {
  return a + b;
}
var curriedAdd = _.curry(add);
var add2 = curriedAdd(2);
add2(1);
// 3

// 替换成

const add = a => b => a + b;
const add2 = add(2);
add2(1);
// 3

这些已curry过的箭头函数对于调试尤其重要。

var lodashAdd = _.curry(function(a, b) {
  return a + b;
});
var add3 = lodashAdd(3);
console.log(add3.length)
// 0
console.log(add3);
//function wrapper() {
//  var length = arguments.length,
//  args = Array(length),
//  index = length;
//
//  while (index--) {
//    args[index] = arguments[index];
//  }…

// 替换成

const es6Add = a => b => a + b;
const add3 = es6Add(3);
console.log(add3.length);
// 1
console.log(add3);
// function b => a + b

如果我们在使用像 lodash/fpramda 这样的函数库,还可以使用箭头函数来代替自调用curry的这种方式。

_.map(_.prop('name'))(people);
// 替换成
people.map(person => person.name);

5. Partial

像应用于curry这样,我们也可以利用箭头函数使partical应用变得容易和明确。

var greet = function(greeting, name) {
  return greeting + ' ' + name;
};

var sayHelloTo = _.partial(greet, 'hello');
sayHelloTo('fred');
// "hello fred"

// 替换成

const sayHelloTo = name => greet('hello', name);
sayHelloTo('fred');
// "hello fred"

还可以传入带有spread操作符的rest参数来部分应用可变函数

const sayHelloTo = (name, ...args) => greet('hello', name, ...args);
sayHelloTo('fred', 1, 2, 3);
// "hello fred"

6. Operators

Lodash提供了许多函数,将函数重新实现为语法操作符,这样就可以将它们传递给集合方法。

在大多数情况下,箭头函数能以足够简短的行内调用实现同样的效果,

_.eq(3, 3);
// true
_.add(10, 1);
// 11
_.map([1, 2, 3], function(n) {
  return _.multiply(n, 10);
});
// [10, 20, 30]
_.reduce([1, 2, 3], _.add);
// 6

// 替换成

3 === 3
10 + 1
[1, 2, 3].map(n => n * 10
[1, 2, 3].reduce((total, n) => total + n);

7. Paths

许多Lodash的函数将路径作为字符串或数组。使用箭头函数可以创建更多可重用的路径。

var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// [3, 4]

_.at(['a', 'b', 'c'], 0, 2);
// ['a', 'c']

//替换成
[
  obj => obj.a[0].b.c,
  obj => obj.a[1]
].map(path => path(object));

[
  arr => arr[0],
  arr => arr[2]
].map(path => path(['a', 'b', 'c']));

因为这些路径"只是函数",我们也可以使它们组合。

const getFirstPerson = people => people[0];
const getPostCode = person => person.address.postcode;
const getFirstPostCode = people => getPostCode(getFirstPerson(people));

我们甚至可以创建可接收参数的高阶路径。

const getFirstNPeople = n => people => people.slice(0, n);

const getFirst5People = getFirstNPeople(5);
const getFirst5PostCodes = people => getFirst5People(people).map(getPostCode);

8. Pick

实用函数pick允许我们从目标对象中选择我们想要的属性。我们可以使用解构和简略的对象字面量实现相同的结果。

var object = { 'a': 1, 'b': '2', 'c': 3 };

return _.pick(object, ['a', 'c']);
// { a: 1, c: 3 }

// 替换成

const { a, c } = { a: 1, b: 2, c: 3 };

return { a, c };

9. Constant,Identity,Noop

Lodash提供了一些实用函数,这些函数可以用于创建具有特定行为的简单函数。

_.constant({ 'a': 1 })();
// { a: 1 }
_.identity({ user: 'fred' });
// { user: 'fred' }
_.noop();
// undefined

我们可以使用箭头函数通过内联方式定义这些函数

const constant = x => () => x;
const identity = x => x;
const noop = () => undefined;

或者我们可以重写上面的例子:

(() => ({ a: 1 }))();
// { a: 1 }
(x => x)({ user: 'fred' });
// { user: 'fred' }
(() => undefined)();
// undefined

10. Chaining & Flow

Lodash提供了一些帮助我们编写链接语句的函数。在多数情况一下,内置的集合方法可以返回一个可以直接链接的数组实例,但是在某些情况下,集合方法会有问题。

但是,我们可以通过一个箭头函数数组定义相同的变换。

_([1, 2, 3])
 .tap(function(array) {
   // Mutate input array.
   array.pop();
 })
 .reverse()
 .value();
// [2, 1]

// 替换成

const pipeline = [
  array => { array.pop(); return array; },
  array => array.reverse()
];

pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);

这样,我们甚至不必去思考tap和thru之间的区别。在实用函数中包含这个reduction,使之成为一个通用的工具。

const pipe = functions => data => {
  return functions.reduce(
    (value, func) => func(value),
    data
  );
};

const pipeline = pipe([
  x => x * 2,
  x => x / 3,
  x => x > 5,
  b => !b
]);

pipeline(5);
// true
pipeline(20);
// false

总结

Lodash终归是一个强大的库,本文只是提供了一个新的视角,以前依赖Lodash实用工具模块的情况下解决的一些问题,如何使用JavaScript演变版本来解决。

不要忽视这些,而是下次你完成一个抽象时,想想是不是一个简单的函数就可以解决问题。

作者的更多文章:

译者郭培尚未开通打赏功能

相关文章