Сохраните своё время, избегая повторного написания этих популярных JavaScript-функций

Рассказывает Флавио Фрейтас


Разработка — это интересный процесс. Но, увлёкшись, мы не всегда выбираем оптимальные решения. Например, когда реализуем функционал, который уже реализован (это называется «изобретать велосипед»). Я хочу познакомить вас с некоторыми функциями, которые многие часто пытаются реализовать самостоятельно, хотя есть путь проще.

Некоторое время назад я начал использовать библиотеки (иногда одну, иногда другую, поскольку они выполняют, в общем-то, одинаковые задачи), в которых реализованы многие функции, используемые регулярно. Эти библиотеки — Underscore и Lodash. Они эффективные и легковесные (~19 КБ).

Список, приведённый ниже, основан на моём опыте работы в проектах. Я описал здесь наиболее распространенные функции, которые вы наверняка использовали раньше.

1. Получить случайный элемент из списка

Это функция, которую я люблю больше всего. С её помощью очень просто заполнить базу данных случайными значениями, что часто бывает необходимо для тестирования.

const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
// Распространенный вариант
function getRandomItem(list){
    return list[Math.floor(Math.random() * (list.length))];
}
getRandomItem(months);
// Underscore и Lodash
_.sample(months);

2. Объединение массивов

Это хороший пример того, как вы можете сократить ваш код и сделать его более читаемым:

const arrayA = [1, 2, 3, 4];
const arrayB = [3, 4, 5, 6];

// Распространенный вариант
const unionArrays = (a, b) => Array.from(new Set([...a, ...b]));

unionArrays(arrayA, arrayB); // [1, 2, 3, 4, 5, 6]

// Underscore и Lodash
_.union(arrayA, arrayB);

3. Пересечение массивов

Другой пример того, как избавиться от кучи кода:

const arrayA = [1, 2, 3, 4, 5];
const arrayB = [2, 3];

// Распространенный вариант
function intersection(a, b) {
  return a.filter(Set.prototype.has, new Set(b));
}
intersection(arrayA, arrayB); // [2, 3]
// Underscore and Lodash
_.intersection(arrayA, arrayB);

4. Избежание итерирования объектов через их свойства

Получение ключей:

const dateEnum = {SHORT: 'DD/MM/YY', LONG: 'DD/MM/YY HH:mm'};

// Распространенный вариант (старый, но распространенный)
var keys = [];
for(var property in dateEnum) {
    if (dateEnum.hasOwnProperty(property)) {
        keys.push(property);
    }
}
console.log(keys); //['SHORT', 'LONG']

// В JavaScript появился новый способ, также очень хороший
Object.keys(dateEnum);

// Underscore и Lodash
_.keys(dateEnum);

Получение значений:

const dateEnum = {SHORT: 'DD/MM/YY', LONG: 'DD/MM/YY HH:mm'};

// Распространенный вариант
var values = [];
for(var property in dateEnum) {
    if (dateEnum.hasOwnProperty(property)) {
        values.push(dateEnum[property]);
    }
}
console.log(values); // ['DD/MM/YY', 'DD/MM/YY HH:mm']

// Новый вариант Javascript (ES8) предоставляет такой способ
Object.values(dateEnum);

// Underscore и Lodash
_.values(dateEnum);

5. Расширение объектов

Довольно часто необходимо скопировать аргументы одного объекта в другой. Решение этой задачи довольно громоздкое:

const user = {name: 'John', occupation: 'Architect'};
const userAddress = {street: 'Times Square Ave', city: 'New York'}

// Распространенный вариант
function copyObject(obj, obj2) {
    for (var key in obj2) {
        obj[key] = obj2[key];
    }
    return obj;
}
copyObject(user, userAddress); // {name: 'John', occupation: 'Architect', street: 'Times Square Ave', city: 'New York'}

// ES6
Object.assign(user, userAddress);

// Underscore
_.extend(user, userAddress);

//Loadash
_.assign(user, userAddress);

6. Цикл в N итераций

Мы часто итерируем по массиву или объекту N раз:

// распространенный вариант
for (var i = 0; i <= 5; i++) {
    // ...
}

// Ещё один
Array.from({length: 5}, (v, k) => {
    // ...
});

// Underscore и Lodash
_.times(5, (obj) => {
    // ...
});

7. Наличие элемента

Функция уже доступна в ES7, но пример пригодится тем, кто ещё не использует эту версию JavaScript:

const array = ['a', 'b', 'c'];

// Распространенный вариант
function contains(array, element) {
   return array.indexOf(element) !== -1;
}
contains(array, 'b'); // true

// ES7
array.includes('b');      

// Underscore
_.contains(array, 'b');

// Lodash
_.includes(array, 'b');

8. Удаление атрибутов из объекта

const user = {name: 'John', occupation: 'Architect', street: 'Times Square Ave', city: 'New York'};

// Распространенный вариант
delete user.occupation;
delete user.street;
user; // {name: 'John', city: 'New York'}

// Underscore и Lodash
_.omit(user, ['occupation', 'street']);

9. Псевдослучайное число из диапазона

Прощайте, длинные функции для получения псевдослучайного числа из диапазона:

// Распространенный вариант
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
getRandom(73, 1014);

// Underscore и Lodash
_.random(73, 1014);

10. Соотношения (map) для вложенных атрибутов

Простой способ итерироваться по массиву для возвращения определённого атрибута объекта. Пример на Lodash и Underscore выглядит примерно одинаково:

const books = [
{
    name: 'Pride and Prejudice',
    author: 'Jane Austen'
},
{
    name: 'Harry Potter and the Prisoner of Azkaban',
    author: 'J.K. Rowling'
}];

// Распространенный вариант
books.map(book => book.name);

// Lodash
_.pluck(books, 'name');

11. Примеси (mixins)

А если после знакомства с функциями Lodash и Underscore вы понимаете, что вам их не хватает, то ваш ответ — примеси. С их помощью вы можете создать собственные функции и расширить функционал библиотеки.

Простой пример:

// Underscore and Lodash
_.mixin({
  capitalize: function(string) {
    return string.charAt(0).toUpperCase() + 
                   string.substring(1).toLowerCase();
  }
});

_.capitalize("john"); // John

БОНУС: Примесь, которую я часто использую в своем коде для получения вложенных атрибутов объекта, не опасаясь, что какое-то значение будет null:

// Underscore
_.mixin({
    getValue(obj, path) {
        if (typeof obj === 'undefined' || obj === null) return;
        path = path.split(/[\.\[\]\"\']{1,2}/);
        for (let i = 0, l = path.length; i < l; i += 1) {
             if (path[i] !== '') {
                 obj = obj[path[i]];
                 if (typeof obj === 'undefined'
                            || obj === null) 
                     return;
                 }
        }
        return obj;
    }
});
const user = {name: 'John', address: [{street: 'River Ave'}]};
_.getValue(user, 'address[0].street'); // River Ave

// Не волнуйтесь о валидации всех внутренних параметров
_.getValue(user, 'occupation[9].company'); // null

// Lodash уже позаботился об этом
_.get(user, 'occupation[9].company'); // null

 

Перевод статьи «Javascript: Save time by avoiding re-writing these common functions»