Малоизвестные функции JavaScript, которые позволят улучшить качество вашего кода

Вступление

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

В этой статье я объясню, как использовать некоторые встроенные функции, которые, несомненно, окажутся для вас полезными. Я расскажу о Debounce, Throttle, Once, Memoize, Curry, Partial, Pipe, Compose, Pick, Omit и Zip, которые вы можете сохранить в служебном файле / классе, чтобы оптимизировать качество вашего кода.

Хотя функции объясняются для JavaScript, они могут быть легко реализованы на любом языке программирования. Как только концепция различных функций будет понята, её можно будет применять везде.

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

Независимо от того, являетесь ли вы новичком или опытным senior-разработчиком, эти функции оптимизируют ваш код. Они сделают работу с JavaScript более приятной и эффективной.

Debounce

Функция Debounce – это метод предотвращения повторной активации функции в результате быстрой серии событий. Она работает путём откладывания выполнения функции до тех пор, пока не пройдет определённый период времени. Функция Debounce – это полезное решение, которое может быть применено в реальных приложениях для повышения производительности за счёт предотвращения выполнения функций, если пользователь быстро нажимает на кнопки.

Следующий фрагмент покажет, как реализовать функцию debounce на JavaScript:

function debounce(func, delay) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), delay);
  };
}

В этом фрагменте JavaScript Debounce вернёт новую функцию, которая выполняет исходную функцию после ранее определённой задержки. Если функция будет вызвана снова, тайм-аут будет сброшен, и вызов функции будет отложен.

Она будет полезна, если у вас есть функция, которая обновляет макет веб-страницы при изменении размера окна. Без функции Debounce эта функция вызывалась бы много раз подряд по мере изменения пользователем размера окна, что может вызвать проблемы с производительностью. С помощью функции Debounce скорость обновления макета может быть ограничена, что делает страницу более отзывчивой и эффективной.

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

// Define the function that updates the layout
function updateLayout() {
  // Update the layout...
}

// Create a debounced version of the function
const debouncedUpdateLayout = debounce(updateLayout, 250);

// Listen for window resize events and call the debounced function
window.addEventListener("resize", debouncedUpdateLayout);

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

Throttle

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

Реализация функции Throttle:

function throttle(func, delay) {
  let wait = false;

  return (...args) => {
    if (wait) {
        return;
    }

    func(...args);
    wait = true;
    setTimeout(() => {
      wait = false;
    }, delay);
  }
}

В этом фрагменте функция Throttle выполнит предоставленную функцию func, обновит переменную wait до значения True, а затем запустит таймер, который сбросит параметр wait после прохождения delay. Если функция Throttle будет вызвана снова, она либо вызовет предоставленную функцию, либо просто вернёт значение, если параметр wait по-прежнему будет иметь значение true.

Она может быть использована на веб-странице, если вы хотите выполнить функцию для обновления макета во время прокрутки. Без Throttle эта функция обновления будет вызываться несколько раз по мере прокрутки страницы пользователем, что приведёт к серьезным проблемам с производительностью. Используя функцию Throttle, вы можете гарантировать, что она будет выполняться только один раз каждые X миллисекунд. Это приведёт к более отзывчивому и эффективному использованию веб-страницы.

В следующем фрагменте вы можете увидеть, как можно использовать функцию Throttle:

// Define the function that updates the layout
function updateLayout() {
  // Update the layout...
}

// Create a throttled version of the function
const throttledUpdateLayout = throttle(updateLayout, 250);

// Listen for window scroll events and call the throttled function
window.addEventListener("scroll", throttledUpdateLayout);

Определив функцию throttleUpdatedLayout и указав задержку в 250 миллисекунд, можно гарантировать, что функция UpdateLayout будет выполняться не чаще одного раза каждые 250 миллисекунд при прокрутке окна. Если событие будет запущено до окончания этого временного промежутка, ничего не произойдет.

Once

Функция Once – это метод, который предотвратит выполнение, если он уже вызван. Это особенно полезно при работе с событиями прослушиваний, где вы часто сталкиваетесь с функциями, которые должны выполняться только один раз. Вместо того, чтобы удалять события прослушиваний каждый раз, вы можете использовать функцию Once в JavaScript.

function once(func) {
  let ran = false;
  let result;
  return function() {
    if (ran) return result;
    result = func.apply(this, arguments);
    ran = true;
    return result;
  };
}

Например, у вас может быть функция, которая отправляет запрос на сервер для загрузки некоторых данных. С помощью функции once() вы могли бы гарантировать, что запрос не будет отправляться несколько раз, если пользователь продолжает нажимать на кнопку. Это позволит избежать проблем с производительностью.

Без функции once() запрос к серверу отправлялся бы столько раз, сколько вы нажмёте на кнопку.

Применение функции once() к любому коду будет выглядеть следующим образом:

// Define the function that sends the request
function requestSomeData() {
  // Send the request...
}

// Create a version of the function that can only be called once
const sendRequestOnce = once(sendRequest);

// Listen for clicks on a button and call the "once" function
const button = document.querySelector("button");
button.addEventListener("click", sendRequestOnce);

В этом примере функция requestSomeData будет вызвана один раз, даже если пользователь нажмёт на кнопку несколько раз.

Memoize

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

function memoize(func) {
  const cache = new Map();
  return function() {
    const key = JSON.stringify(arguments);
    if (cache.has(key)) {
        return cache.get(key);
    }
    const result = func.apply(this, arguments);
    cache.set(key, result);
    return result;
  };
}

Функция memoize() кэширует результат данной функции и использует аргументы в качестве ключа для извлечения результата при повторном вызове с теми же аргументами.

Теперь, если у вас есть функция, которая выполняет сложное вычисление, основанное на входной переменной, вы можете использовать функцию memoize() для кэширования результатов и мгновенного их извлечения при многократном вызове с одним и тем же вводом.

Чтобы увидеть преимущества функции memoize(), вы можете использовать её для вычисления чисел Фибоначчи:

// Define the function that performs the calculation
function fibonacci(n) {
    if (n < 2)
        return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// Create a memoized version of the function
const memoizedFibonacci = memoize(fibonacci);

// Call the memoized function with multiple input values
console.time('total')
console.time('sub1')
const result1 = memoizedFibonacci(30);
console.timeEnd('sub1')
console.time('sub2')
const result2 = memoizedFibonacci(29);
console.timeEnd('sub2')
console.time('sub3')
const result3 = memoizedFibonacci(30);
console.timeEnd('sub3')
console.timeEnd('total')

В этом примере функция fibonacci() будет преобразована в функцию memoizedFibonacci. Затем будет вызвана функция memoized(), и время выполнения будет занесено в консоль.

Результат будет выглядеть следующим образом:

Малоизвестные функции JavaScript, которые позволят улучшить качество вашего кода

Хотя второй вызов вычисляет только число Фибоначчи 29, это заняло намного больше времени, чем вычисление числа Фибоначчи 30 во второй раз, поскольку оно было кэшировано функцией memoize().

Curry

Функция Curry (также известная как Currying) – это продвинутая функция JavaScript, используемая для создания новой функции из существующей путем “предварительного заполнения” некоторых её аргументов. Currying часто используется при работе с функциями, которые принимают несколько аргументов, и преобразуют их в функции, которые принимают одни аргументы, потому что другие всегда остаются неизменными.

Использование функции Curry имеет несколько преимуществ:

  • Она помогает избежать повторного использования одной и той же переменной
  • Она делает код более читабельным
  • Она делит функции на множество более мелких функций
function curry(func, arity = func.length) {
  return function curried(...args) {
    if (args.length >= arity) return func(...args);
    return function(...moreArgs) {
      return curried(...args, ...moreArgs);
    };
  };
}

Эта функция принимает другую функцию (func) и необязательный параметр arity, который по умолчанию равен длине аргументов func. Она возвращает новую функцию (curried), которая может быть вызвана с заданным числом аргументов. Если были предоставлены не все аргументы, она возвращает новую функцию, которая может быть вызвана с большим количеством аргументов до тех пор, пока не будут предоставлены все требуемые аргументы. Когда будут предоставлены все аргументы, вызывается исходная функция (func), и будет возвращён её результат.

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

Следующий фрагмент покажет, как ранее определенная функция Curry используется для оптимизации удобочитаемости:

// Define the function that calculates the distance between two points
function distance(x1, y1, x2, y2) {
  return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}

// Create a curried version of the function that only requires one of the points
const distanceFromOrigin = curry(distance, 3)(0, 0);

// Call the curried function with the other point
const d1 = distanceFromOrigin(1, 1);
const d2 = distanceFromOrigin(2, 2);

В этом примере создается обработанная версия функции distance (distanceFromOrigin) с использованием функции Curry и передачей distance в качестве первого аргумента и 3 в качестве второго аргумента (arity). Кроме того, она вызовет функцию curried с 0,0 в качестве первых двух аргументов.

Результирующая функция distanceFromOrigin теперь является новой функцией, которой требуется всего два аргумента, и она всегда будет использовать 0,0 в качестве первой точки.

Partial

Функция Partial в JavaScript аналогична функции Curry. Существенное различие между Curry и Partial заключается в том, что вызов функции Partial возвращает результат мгновенно вместо возврата другой функции по currying -цепочке.

function partial(func, ...args) {
  return function partiallyApplied(...moreArgs) {
    return func(...args, ...moreArgs);
  }
}

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

В следующем примере использования функция calculate будет предварительно заполнена первыми двумя аргументами, чтобы сгенерировать новую функцию с более читаемым именем:

// Define the function that calculates something
function calculate(x, y, z) {
 return (x + y) * z
}

// Create a partially applied version of the function the last argument
const multiply10By = partial(calculate, 8, 2);

// Call the partially applied function with the number of iterations
const result = multiply10By(5);

В этом примере функция multiply10By создаётся путем частичного применения общей функции calculate и предварительного заполнения первых двух аргументов 8 и 2. Это создаст новую функцию multiply10By, для которой требуется только один аргумент, указывающий количество умножения на 10, которое должно быть выполнено. Кроме того, это сделает код более читаемым и понятным.

Pipe

Функция Pipe – это служебная функция, используемая для объединения в цепочку нескольких функций и передачи выходных данных одной из них. Она похожа на оператор Unix pipe и будет применять все функции слева направо с помощью функции JavaScript reduce():

function pipe(...funcs) {
  return function piped(...args) {
    return funcs.reduce((result, func) => [func.call(this, ...result)], args)[0];
  };
}

Чтобы понять функцию Pipe, представьте, что у вас есть три функции:

  • добавить Prefix к String
  • добавить Suffix к String
  • преобразовать строку в верхний регистр

Затем вы можете использовать функцию pipe для создания новой функции, которая будет применять всё это слева направо к строке.

// Define the functions that add to the string
function addPrefix(str) {
  return "prefix-" + str;
}

function addSuffix(str) {
  return str + "-suffix";
}

function toUppercase(str) {
    return str.toUpperCase()
}

// Create a piped function that applies the three functions in the correct order
const decorated1 = pipe(addPrefix, addSuffix, toUppercase);
const decorated2 = pipe(toUppercase, addPrefix, addSuffix);

// Call the piped function with the input string
const result1 = decorated1("hello");  // PREFIX-HELLO-SUFFIX
const result2 = decorated2("hello");  // prefix-HELLO-suffix

В этом примере функции decorated1 и decorated2 создаются путём передачи функций addPrefix, addSuffix и toUpperCase в разном порядке. Созданные новые функции могут быть вызваны с помощью входной строки, чтобы применить три исходные функции в заданном порядке. Результирующие выходные строки будут отличаться, поскольку порядок, указанный в функции pipe, отличается.

Compose

Функция Compose такая же, как и функция Pipe, но она будет использовать reduceRight для применения всех функций:

function compose(...funcs) {
  return function composed(...args) {
    return funcs.reduceRight((result, func) => [func.call(this, ...result)], args)[0];
  };
}

Это приведёт к той же функциональности, но функции будут применяться справа налево.

Pick

Функция Pick в JavaScript используется для выбора определённых значений из объекта. Это способ создать новый объект, выбрав определённые свойства из предоставленного проекта. Это метод функционального программирования, который позволяет извлекать подмножество свойств из любого объекта, если эти свойства доступны.

Вот реализация функции Pick:

function pick(obj, keys) {
  return keys.reduce((acc, key) => {
    if (obj.hasOwnProperty(key)) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
}

Эта функция принимает два параметра:

  • obj: Исходный объект, из которого будет создан новый объект
  • keys: Массив ключей для выбора в новом объекте.

Чтобы создать новый объект, функция будет использовать метод reduce() для перебора ключей и сравнения их со свойствами исходного объекта. Если значение присутствует, оно будет добавлено к объекту accumulator функции reduce, которая была инициализирована с помощью {}.

В конце функции reduce объект accumulator станет новым объектом и будет содержать только указанные свойства, которые были в массиве keys.

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

const obj = {
    id: 1,
    name: 'Paul',
    password: '82ada72easd7',
    role: 'admin',
    website: 'https://www.paulsblog.dev',
};

const selected = pick(obj, ['name', 'website']);
console.log(selected); // { name: 'Paul', website: 'https://www.paulsblog.dev' }

Эта функция будет использовать функцию pick() для создания нового объекта, содержащего только name и website , который может быть возвращён вызывающему без предоставления role, password или id.

Omit

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

function omit(obj, keys) {
  return Object.keys(obj)
    .filter(key => !keys.includes(key))
    .reduce((acc, key) => {
      acc[key] = obj[key];
      return acc;
    }, {});
}

Эта функция принимает 2 аргумента:

  • obj: Исходный объект, из которого будет создан новый объект
  • keys: массив ключей, которых не будет в новом объекте.

Чтобы создать новый объект и удалить свойства, функция Object.keys() используется для создания массива ключей для исходного объекта. Затем функция JavaScript filter() удалит каждый ключ, который был указан в аргументе keys. С помощью функции reduce будут повторены остальные ключи, и будет возвращён новый объект, который состоит только из каждого ключа, не предоставленного в массиве keys.

На практике вы можете использовать её, если у вас есть большой пользовательский объект, где вы хотите удалить только идентификатор:

const obj = {
    id: 1,
    name: 'Paul',
    job: 'Senior Engineer',
    twitter: 'https://www.twitter.com/paulknulst',
    website: 'https://www.paulsblog.dev',
};

const selected = omit(obj, ['id']);
console.log(selected); // {name: 'Paul', job: 'Senior Engineer', twitter: 'https://www.twitter.com/paulknulst', website: 'https://www.paulsblog.dev'}

В этом примере функция omit() используется для удаления свойства id и извлечения объекта, который сделает ваш код более читаемым, чем при использовании цикла for, установив obj.id = undefined  или с использованием pick() и предоставлением каждого атрибута для выбора.

Zip

Функция Zip – это функция JavaScript, которая сопоставляет каждый переданный массив элементов с другим элементом массива и используется для объединения нескольких массивов в единый массив кортежей. Результирующий массив будет содержать соответствующие элементы из каждого массива. Часто эта функция используется при работе с данными из нескольких источников, которые необходимо каким-либо образом объединить или соотнести.

Реализация этой функции проста:

function zip(...arrays) {
  const maxLength = Math.max(...arrays.map(array => array.length));
  return Array.from({ length: maxLength }).map((_, i) => {
    return Array.from({ length: arrays.length }, (_, j) => arrays[j][i]);
  });
}

Этот фрагмент JavaScript создаст новый массив массивов, где каждый подмассив состоит из элементов предоставленных массивов. Это означает, что каждый элемент исходного массива будет сопоставлен другому элементу из другого исходного массива с тем же индексом.

Например, у вас могло бы быть три массива, которые:

  • содержат координату x
  • содержат координату y
  • содержат координату z

Без функции zip вы бы вручную перебирали массивы и соединяли элементы x, y и z в пары. Но, используя функцию zip, вы можете передать исходные массивы и сгенерировать новый массив кортежей (x, y, z).

// Define the arrays that contain the coordinates
const xCoordinates = [1, 2, 3, 4];
const yCoordinates = [5, 6, 7, 8];
const zCoordinates = [3, 6, 1, 7];

// Create a zipped array of points
const points = zip(xCoordinates, yCoordinates, zCoordinates);

// Use the zipped array of points
console.log(points);  // [[1, 5, 3], [2, 6, 6], [3, 7, 1], [4, 8, 7]]

В этом примере функция zip используется для объединения массивов xCoordinates, yCoordinates и zCoordinates в единый массив кортежей.

Заключение

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

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

+1
1
+1
4
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *