十个可以用ES6替代的Lodash特性

原文地址: 10 Lodash Features You Can Replace with ES6
作者: Dan Prince

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]

// 使用ES6
[1, 2, 3].map(n => n * 3);
[1, 2, 3].reduce((total, n) => total + n);
[1, 2, 3].filter(n => n <= 2);

不止如此,如果我们使用现代浏览器,还可以用findsomeeveryreduceRight来代替lodash中相应的方法。

2. Head & Tail

可以用解构赋值来获取列表的第一个数据和剩余数据,而不需要工具函数来实现。

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

// 使用ES6
const [head, ...tail] = [1, 2, 3];

同样也能用类似的方法获得开头的和最后一个数据

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

// 使用ES6
const [last, ...initial] = [1, 2, 3].reverse();

如果不希望改变原来的数据结构,可以在调用reverse方法前使用扩展运算符克隆原数组

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

3. Rest和Spread

rest和spread函数允许我们定义和调用接受不定数量参数的函数。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"

// 使用ES6

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

javascript不像一些高级语言如TypeScript和Flow那样定义函数类型签名,这让柯里化的实现变得困难。当接收柯里化的函数时,很难知道有多少参数已经被提供了,之后还需要提供哪些。通过使用箭头函数,就可以更直观的定义柯里化函数,让代码更容易理解。

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

// 使用ES6

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

使用箭头函数也让代码调试变得直观

var lodashAdd = _.curry(function(a, b) {
  return a + b;
});
var add3 = lodashAdd(3);
console.log(add3.length)
// 0
console.log(add3);
// function (a, b) {
// /* [wrapped with _.curry & _.partial] */
//   return a + b;
// }

// 使用ES6

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

如果使用像lodash/fp或者ramda的函数库,也可以用箭头函数取代自动柯里化的风格代码

_.map(_.prop('name'))(people);

// 使用ES6

people.map(person => person.name);

5. Partial

和柯里化类似,我们可以使用箭头函数让partial功能变得简单直观

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

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

// 使用ES6

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

也可以使用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

// 使用ES6
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']

// 使用ES6
[
  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方法允许我们从目标对象中选择需要的属性。在ES6中可以使用解构和对象属性简洁表示法来实现同样的目的。

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

// 使用ES6
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]

// ES6实现:
const pipeline = [
  array => { array.pop(); return array; },
  array => array.reverse()
];

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

这样,我们甚至不用考虑tapthru的不同。
使用工具函数包裹reduce方法创建一个更好的通用工具。

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依然是一个很棒的工具库,这里只是提供了一个新的视角,介绍如何在之前使用模块工具的情况下使用新一代JavaScript来解决同样的问题。

不要忽视它,下次遇到一个抽象方法时,可以试想如何用一个简单函数来替代它。

Comments
Write a Comment