Description
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]
// becomes
[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, every 和 reduceRight。
2. Head & Tail
解构语法 (Destructuring syntax) 允许我们可以在不使用 utility 函数取得一个列表的 head 和 tail。
_.head([1, 2, 3]);
// 1
_.tail([1, 2, 3]);
// [2, 3]
// becomes
const [head, ...tail] = [1, 2, 3];
也可以使用相似的方式获取 initial 的元素和 last 元素。
_.initial([1, 2, 3]);
// -> [1, 2]
_.last([1, 2, 3]);
// 3
// becomes
const [last, ...initial] = [1, 2, 3].reverse();
如果你因为 reverse 方法改变数据结构而烦恼,可以在调用 reverse 之前使用展开操作克隆数组。
const xs = [1, 2, 3];
const [last, ...initial] = [...xs].reverse();
3. Rest & Spread
rest 和 spread 方法允许我们定义和调用接收可变参数的函数。ES6 为这些操作引入 dedicated syntaxes。
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"
// becomes
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
不像一些高级别的语言例如 TypeScript 或 Flow,JS 无法提供 function 的类型信息,这使得柯里化相当困难。 当我们拿到一个柯里化的函数,很难知道有多少参数已经被提供和下次我们需要提供那些参数。使用箭头函数我们可以明确定义柯里化函数,使它们易于被其他程序员理解。
function add(a, b) {
return a + b;
}
var curriedAdd = _.curry(add);
var add2 = curriedAdd(2);
add2(1);
// 3
// becomes
const add = a => b => a + b;
const add2 = add(2); // 与上面比很明显 a 是 2
add2(1); //与上面比很明显 b 是 1
// 3
使用箭头函数定义的柯里化函数特别适合调试。
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];
// }…
// becomes
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);
// becomes
people.map(person => person.name);
5. Partial
和柯里化一样,我们可以使用箭头函数使偏函数应用 (partial application) 变得容易和明确。
var greet = function(greeting, name) {
return greeting + ' ' + name;
};
var sayHelloTo = _.partial(greet, 'hello');
sayHelloTo('fred');
// "hello fred"
// becomes
const sayHelloTo = name => greet('hello', name);
sayHelloTo('fred');
// "hello fred"
它也可能通过展开操作将剩余参数用于可变参数函数。
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
// becomes
3 === 3
10 + 1
[1, 2, 3].map(n => n * 10);
[1, 2, 3].reduce((total, n) => total + n);
7. Paths
许多 lodash 的方法,将 path 看做字符串或者数组。我们可以使用箭头函数创建更可复用 path。
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']
// becomes
[
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']));
由于这些 path “恰恰类似” 函数,我们也可以把它们组装成如下形式:
const getFirstPerson = people => people[0];
const getPostCode = person => person.address.postcode;
const getFirstPostCode = people => getPostCode(getFirstPerson(people));
甚至可以做出高优先级且带参数的 path。
const getFirstNPeople = n => people => people.slice(0, n);
const getFirst5People = getFirstNPeople(5);
const getFirst5PostCodes = people => getFirst5People(people).map(getPostCode);
8. Pick
pick 允许我们从一个目标对象中选择我们想要的属性。我们可以使用解构(destructuring)和简写对象字面量(shorthand object literals)达到相同的结果。
var object = { 'a': 1, 'b': '2', 'c': 3 };
return _.pick(object, ['a', 'c']);
// { a: 1, c: 3 }
// becomes
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
We can define all of these functions inline using arrows.
const constant = x => () => x;
const identity = x => x;
const noop = () => undefined;
Or we could rewrite the example above as:
(() => ({ a: 1 }))();
// { a: 1 }
(x => x)({ user: 'fred' });
// { user: 'fred' }
(() => undefined)();
// undefined
10. Chaining & Flow
Lodash 提供一些函数帮助我们写出链式语句。在多数情况中,原生的集合方法返回一个可以直接被链式操作的数组实例,但是在一些情况中,方法改变了集合,链式操作就不太可能了。(but in some cases where the method mutates the collection, this isn’t possible. 这句不好翻译,求帮助?)
然而,我们可以用箭头函数数组定义相同的变换。
_([1, 2, 3])
.tap(function(array) {
// Mutate input array.
array.pop();
})
.reverse()
.value();
// [2, 1]
// becomes
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
使用这种方式,我们甚至都不需要考虑 tap 和 thru 的不同。在函数中封装 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 如何让我们解决一些在之前依赖 utility 模块解决的问题。
原文地址:
https://www.sitepoint.com/lodash-features-replace-es6/?utm_source=javascriptweekly&utm_medium=email
Activity