5 недооцененных возможностей JavaScript

5 недооцененных возможностей JavaScript

https://t.me/javascriptv

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

Итак, предлагаю ознакомиться с одними из самых недооцененных возможностей TypeScript.

1. FlatMap

FlatMap в JavaScript — это отличная техника, с которой можно познакомиться здесь. FlatMap объединяет в себе функции map и метода массивов filter. Рекомендую использовать flatMap() вместо комбинации filter() и map().

В ОТЛИЧИЕ ОТ КОМБИНАЦИИ FILTER() И MAP(), FLATMAP ДЕЛАЕТ ОДИН ПРОХОД И НЕ СОЗДАЕТ ПРОМЕЖУТОЧНОГО МАССИВА.
// использование filterAndMap
console.time("filterAndMap")
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const squaredOddNumbers = numbers
    .filter(num => num % 2 !== 0)
    .map(num => num * num)

console.log(squaredOddNumbers); // [1, 9, 25, 49, 81]
console.timeEnd("filterAndMap")
Использование filter и Map
console.time("filterAndMap")
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const squaredOddNumbers = numbers.flatMap(num => 
    num % 2 !== 0 ? [num * num] : []
);


console.log(squaredOddNumbers); // [1, 9, 25, 49, 81]
console.timeEnd("filterAndMap")
Использование FlatMap


2. Порядок использования методов массивов

Методы массивов — одни из самых важных методов, помогающих взаимодействовать с массивами. В JavaScript существует множество методов массивов. Наиболее популярные из них — .filter(), .find(), .map(), .reduce(). Их можно объединять в шаблоны:

// метод массива, который сортирует массив 
// только по нечетным числам и возводит их в 3-ю степень
numbers
  .sort((a, b) => a - b)
  .filter((n) => n % 2 !== 0)
  .map((n) => n ** 3);

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

Оптимальный код для приведенного выше случая выглядит так:

const numbers = [9, 3, 6, 4, 8, 1, 2, 5, 7];

// метод массива, который сортирует массив
// только по нечетным числам и возводит их в 3-ю степень
numbers
  .filter((n) => n % 2 !== 0)
  .sort((a, b) => a - b)
  .map((n) => n ** 3);

3. Метод reduce

Я наблюдал эту проблему у многих фронтенд-разработчиков. Когда пакет типа react-charts запрашивает данные в объектоподобной структуре, а реализация react-charts запрашивает данные в формате, сгруппированном по ключу, большинство разработчиков применяют метод .forEach() или некорректно используют метод map(). Например, так:

fetch("https://jsonplaceholder.typicode.com/todos/")
  .then(res=>res.json())
  .then(todos=>{
    
    // использование Map
    const todosForUserMap = {};
    todos.forEach(todo=>{
      if (todosForUserMap[todo.userId]){
        todosForUserMap[todo.userId].push(todo);  
      }else{
        todosForUserMap[todo.userId] = [todo];
      }  
    })

console.log(todosForUserMap)
  })

Этот метод хорош тем, что в нем используется метод forEach, а не map. Здесь не стоит использовать метод map, так как для каждого элемента будет создан массив. Скажем, если массив состоит из 1000 записей, то в map будет создан массив нулевой длины из 1000 записей, а в forEach() такого создания массива не произойдет.

По сравнению с методами, показанными выше, более чистым и читабельным подходом является метод массива reduce. Приведенный выше код исправлен следующим образом:

fetch("https://jsonplaceholder.typicode.com/todos/")
  .then(res=>res.json())
  .then(todos=>{
    
    // использование Map
    const todosForUserMap = todos.reduce((accumulator, todo)=>{
      if (accumulator[todo.userId]) accumulator[todo.userId].push(todo);
      if (!accumulator[todo.userId]) accumulator[todo.userId] = [todo];
      return accumulator;
    },{})

console.log(todosForUserMap)
  })

Метод reduce позволяет не создавать лишних массивов и является гораздо более ясным и удобным в использовании. Несмотря на то что он похож на forEach(), рекомендую использовать именно его, так как он более чистый и понятный.

4. Генераторы

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

КАК БЫ ВЫ РЕАЛИЗОВАЛИ ФУНКЦИЮ БЕСКОНЕЧНОЙ ЗАГРУЗКИ В NODEJS-СЕРВЕРЕ ИЛИ ВАНИЛЬНОМ JAVASCRIPT?

Вот где итераторы действительно полезны. Используйте их, вместо того чтобы передавать поток данных в запросе в локальное хранилище или куда-то еще, откуда их можно извлечь для последующего применения. Можно сделать это с помощью асинхронных генераторов. Так вы справитесь с проблемой бесконечной загрузки в JS.

async function *fetchProducts(){
  while (true){
    const productUrl = "https://fakestoreapi.com/products?limit=2";
    const res = await fetch(productUrl)
    const data = await res.json()
    yield data;
    // манипулируйте UI как здесь
    // или сохраните его в БД или в другом месте;
    // используйте его в качестве места для побочных эффектов;
    // даже если какое-то условие совпадает, прерывайте поток
  }
}

async function main() {
  const itr = fetchProducts();
  // должно вызываться в зависимости от взаимодействия с пользователем,
  // или же используйте другие приемы, чтобы избежать бесконечной загрузки.
  console.log( await itr.next() );
}
return main()

5. Нативные классы JavaScript

В комплект поставки JavaScript входят нативные классы, с помощью которых можно довольно легко создавать/инстанцировать такие элементы, как URL, заголовки и т. д. Вам наверняка доводилось видеть, как кто-то пытается запросить параметры в URL следующим образом:

async function getUrl(userId, limit, category){
  return `https://fakestoreapi.com/products${category ? `/category/${category}` : ""}${limit ? Number(limit):""}${userId? Number(userId):""}`;    
}

Приведенный выше код является неаккуратным и, скорее всего, приведет к сбою программы. Кроме того, он потребует добавления какого-либо правила в конце каждый раз, и нужно будет добавлять какой-либо другой параметр. Используя нативный класс типа URL, можно исправить ситуацию. Улучшенный код приведен ниже:

function constructURL(category, limit, userId) {
  const baseURL = "https://fakestoreapi.com/products";
  const url = new URL(baseURL);
  const params = new URLSearchParams();

if (category) url.pathname += `/category/${category}`;
  if (limit) params.append('limit', Number(limit).toString());
  if (userId) params.append('userId', Number(userId).toString());

  url.search = params.toString();
  return url.toString();
}

Таким образом, можно обрабатывать сложные условия создания URL в рамках одного файла. Здесь объект URL следует BuilderPattern — одному из многих паттернов проектирования, который можно реализовать в коде, чтобы спрятать сложную логику в отдельном месте и улучшить читабельность.


Источник


Report Page