Статьи

10 функций Lodash, которые вы можете заменить на ES6

На данный момент Lodash является наиболее зависимым пакетом npm , но если вы используете ES6, он может вам и не понадобиться.

В этой статье мы рассмотрим использование собственных методов сбора данных с функциями стрелок и другими новыми функциями ES6, чтобы помочь нам обойти многие популярные варианты использования.


1. Карта, фильтр, уменьшить

Эти методы сбора делают преобразование данных быстрым и практически универсальным. Мы можем связать их с функциями стрелок, чтобы помочь нам написать краткие альтернативы реализациям, предлагаемым 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);

Здесь это не останавливается. Если мы используем современный браузер, мы также можем использовать find , some , each и reduRight .

2. Голова и хвост

Разрушающий синтаксис позволяет нам получить начало и конец списка без вспомогательных функций:

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

// becomes

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

Также возможно получить начальные элементы и последний элемент аналогичным образом:

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

// becomes

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

Если вас раздражает, что реверс изменяет структуру данных, то вы можете использовать оператор распространения, чтобы клонировать массив перед вызовом реверса:

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

3. Отдых и распространение

Остальные и расширенные функции позволяют нам определять и вызывать функции, которые принимают переменное число аргументов. 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"

// 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. Карри

Без языка более высокого уровня, такого как [TypeScript] [5] или [Flow] [6], мы не можем дать нашим сигнатурам типа функций, что делает каррирование довольно трудным. Когда мы получаем функции карри, трудно узнать, сколько аргументов уже было предоставлено, и что нам нужно будет предоставить дальше. С помощью функций со стрелками мы можем явно определять функции карри, облегчая их понимание для других программистов:

 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);
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;
// }

// 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. Частичное

Как и при каррировании, мы можем использовать функции стрелок, чтобы сделать частичное применение простым и понятным:

 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. Операторы

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. Пути

Многие из функций 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']

// 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']));

Поскольку эти пути являются «просто функциями», мы также можем их составить:

 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 позволяет нам выбирать нужные свойства целевого объекта. Мы можем достичь тех же результатов, используя деструктурирование и сокращенные объектные литералы:

 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. Константа, идентичность, ноуп

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. Цепочка и поток

Lodash предоставляет некоторые функции, помогающие нам писать цепочки операторов. Во многих случаях встроенные методы коллекции возвращают экземпляр массива, который может быть напрямую связан, но в некоторых случаях, когда метод изменяет коллекцию, это невозможно.

Тем не менее, мы можем определить те же преобразования, что и массив функций стрелок:

 _([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]);

Таким образом, нам даже не нужно думать о разнице между нажатием и через . Заключение этого сокращения в служебную функцию делает отличный инструмент общего назначения:

 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 позволяет нам решать некоторые проблемы в ситуациях, когда мы ранее полагались на служебные модули.

Не игнорируйте это. Вместо этого, в следующий раз, когда вы достигнете абстракции, подумайте, будет ли вместо этого простая функция!

Эта статья была рецензирована Марком Брауном . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!