Рубрики
Без рубрики

Почему все статьи о демистификации методов массива JS являются мусором

Оригинальный пост: https://siderite.dev/blog/why-all-articles-about-demystifying-js-array-metho/ Каждый… Tagged с C, JavaScript, программированием, разглагольствованием.

Оригинальный пост: https://siderite.dev/blog/why-all-articles-about-demystify-js-array-metho/

Каждый месяц или около того я вижу другую статью, опубликованную какой -то разработкой, обычно с запоминающимся заголовком, использующим такие слова, как «демистификация» или «понимание» или «n массивных методов, которые вы должны использовать» или «упростить свой JavaScript или что -то подобное. Это стало настолько обыденным и скучным, что меня злится, кто -то все еще пытается кэшировать эти уставшие идеи, чтобы попытаться казаться умным. Так что перестань делать это! Но это становится хуже. Эти статьи частично вводят в заблуждение, потому что JavaScript развивался после необходимости получать или возвращать данные в качестве массивов. Позвольте мне демистифицировать вас.

Прежде всего, методы, которые мы обсуждаем здесь, .filter и .карта . Есть, конечно .уменьшать , но это не обязательно возвращает массив. По иронии судьбы, можно написать оба .filter и .map как функция уменьшения, так что исправьте ее, и вы можете уйти далеко. Есть также .sort что по соображениям производительности работает немного по -другому и ничего не возвращает, поэтому его нельзя приковать, как другие могут. Все эти методы из объекта массива имеют что -то общее: они получают функции в виде параметров, которые затем применяются ко всем элементам в массиве. Прочитайте это еще раз: Все элементы Анкет

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

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

var colors = [
  { name: 'red', R: 255, G: 0, B: 0 },
  { name: 'blue', R: 0, G: 0, B: 255 },
  { name: 'green', R: 0, G: 255, B: 0 },
  { name: 'pink', R: 255, G: 128, B: 128 }
];

// it would be more efficient to get the reddish colors in an array
// and sort only those, but we want to discuss chaining array methods
colors.sort((c1, c2) => c1.name > c2.name ? 1 : (c1.name < c2.name ? -1 : 0));

const result = colors
  .filter(c => c.R > c.G && c.R > c.B)
  .slice(page * pageSize, (page + 1) * pageSize)
  .map(c => ({
      name: c.name,
      color: `#${hex(c.R)}${hex(c.G)}${hex(c.B)}`
  }));

Этот код принимает кучу цветов, которые имеют значения RGB и имя, и возвращает страницу (определяемую страницей и страницей) из цветов, которые являются «красноватыми» (более красными, чем синим и зеленым) по имени. Полученные объекты имеют имя и цветную строку HTML.

Это работает для множества четырех элементов, он отлично работает и для массивов из тысяч элементов, но давайте посмотрим на то, что он делает:

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

Давайте напишем это классическим образом, с петлями, чтобы увидеть, как это работает:

const result = [];
let i=0;
for (const c of colors) {
    if (c.R=pageSize) break;
}

И это делает это:

  • он итерации через массив цветов, но у него есть условие выхода
  • он игнорирует не красноватые цвета
  • он игнорирует цвета предыдущих страниц, но нигде их не храня
  • Он хранит красноватые цвета в результате как их преобразованная версия напрямую
  • он выходит из петли, если результат – это размер страницы, таким образом, только проходит ( Страница+1)*Петли страниц

Нет дополнительных массивов, нет дополнительных итераций, только какой -то уродливый код задницы. Но что, если бы мы могли написать это так же хорошо, как в первом примере, и заставить его работать так же эффективно, как и второе? Из -за Ecmascript 6 мы действительно можем!

Взгляните на это:

const result = Enumerable.from(colors)
  .where(c => c.R > c.G && c.R > c.B)
  //.orderBy(c => c.name)
  .skip(page * pageSize)
  .take(pageSize)
  .select(c => ({
      name: c.name,
      color: `#${hex(c.R)}${hex(c.G)}${hex(c.B)}`
  }))
  .toArray();

Что это за перечисление? Это класс, который я сделал, чтобы инкапсулировать методы. Где,. Skip, .take and. Select и рассмотрю его позже. Почему эти имена? Потому что они отражают аналогичные имена методов в LINQ (интегрированные языковые запросы от .net) и потому что я хотел четко отделить их от методов массива.

Как все это работает? Если вы посмотрите на «классическую» версию кода, вы видите новый Для .. of Loop введено в ES6. Он использует концепцию «итерабильного», чтобы пройти все элементы, которые он содержит. Массив является итерабильной, но также является функцией генератора, а также конструкцией ES6. A Функция генератора это функция, которая генерирует значения по своему усмотрению, преимуществом в том, что ей не нужно держать все элементы в памяти (например, массив), и любая операция, которая требует выполнения значений, выполняется только на тех, которые запрашивались код.

Вот что делает вышеупомянутый код:

  • Он создает перечисленную обертку по массиве (не выполняет операцию, просто назначения)
  • он фильтрует, определяя функцию генератора Это возвращает только красноватые цвета (но не выполняет работу) и возвращает перечисленную обертку над функцией
  • Он игнорирует элементы с предыдущих страниц, определяя функцию генератора, которая подсчитывает элементы и возвращает элементы только после указанного числа (опять же, без работы) и возвращает перечисляемую обертку над функцией
  • Затем он берет страницу, полную элементов, останавливаясь сразу после этого, определяя функцию генератора, которая выполняет это (без работы) и возвращает перечисленную обертку над функцией
  • Он преобразует цвета в выходных элементах, определяя функцию генератора, которая отражает существующие элементы и возвращает преобразованные значения (без работы) и возвращает перечисляемую обертку над функцией
  • Он отражает функцию генератора в текущем перечислении и заполняет массив значениями (все операции выполняются здесь)

А вот поток для каждого элемента:

  1. .toarray перечисляет функцию генератора. Выберите
  2. . Выберите перечисление функции генератора .take
  3. .take перечисляет функцию генератора .skip
  4. .skip перечисляет функцию генератора. Где
  5. . где перечисляет функцию генератора, которая итерация на массиве цветов
  6. Первый цвет красный, который красный, так что. Где «дает» его, он проходит как следующий элемент на итерации
  7. Страница 0, скажем так, так что .skip нечего пропустить, она дает цвет
  8. .take по -прежнему есть предметы для размера страниц, давайте предположим 20, так что он дает цвет
  9. .Выбрать дает цвет, преобразованный для вывода
  10. .toarray выталкивает цвет в результате
  11. Перейти к 1.

Если по какой -то причине вам понадобится только первый элемент, а не всю страницу (представьте себе использование первого метода. Только шаги от 1. до 10. будут выполнены. Нет дополнительных массивов, нет дополнительной фильтрации, картирования или назначения.

Я слишком стараюсь казаться умным? Что ж, представьте, что есть три миллиона цветов, треть из них красноватые. Первый код создаст массив из миллиона предметов, итерация и проверки всех трех миллионов цветов, а затем извлечь из этого кусок страницы (еще один массив, какой бы небольшой), а затем создаст еще один массив сопоставленных объектов. Этот код? Это эквивалент классической, но с чрезвычайной читаемостью и простотой использования.

ОК, что это за. то, что я прокомментировал? Это возможный метод, который заказывает элементы в Интернете, как они приходят, в момент исполнения (поэтому, когда выполняется .toarray). Это слишком сложно для этого поста в блоге, но есть полная реализация перечисленного, которое я написал, содержащий все, что вам когда -либо понадобится. В этом случае .Orderby будет заказать только минимальное количество элементов, необходимых для извлечения страницы ((Page+1) * PageSize). Реализация может использовать пользовательские алгоритмы сортировки, которые учитывают операторы.

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

На самом деле не нужна перечисленная обертка, и может также добавлять методы к прототипу всех функций генератора (см. подобные LINQ в JavaScript с отложенным выполнением ). Как видите, это было написано 5 лет назад, и до сих пор люди “учат” другим, что. Являются ли JavaScript эквивалентами . Где и . Select от .net. Нет, они не!

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

Вот код для упрощенного перечисленного объекта, используемого для этого поста:

class Enumerable {
  constructor(generator) {
    this.generator = generator || function* () { };
  }

  static from(arr) {
    return new Enumerable(arr[Symbol.iterator].bind(arr));
  }

  where(condition) {
    const generator = this.generator();
    const gen = function* () {
      let index = 0;
      for (const item of generator) {
        if (condition(item, index)) {
          yield item;
        }
        index++;
      }
    };
    return new Enumerable(gen);
  }

  take(nr) {
    const generator = this.generator();
    const gen = function* () {
      let nrLeft = nr;
      for (const item of generator) {
        if (nrLeft > 0) {
          yield item;
          nrLeft--;
        }
        if (nrLeft <= 0) {
          break;
        }
      }
    };
    return new Enumerable(gen);
  }

  skip(nr) {
    const generator = this.generator();
    const gen = function* () {
      let nrLeft = nr;
      for (const item of generator) {
        if (nrLeft > 0) {
          nrLeft--;
        } else {
          yield item;
        }
      }
    };
    return new Enumerable(gen);
  }

  select(transform) {
    const generator = this.generator();
    const gen = function* () {
      for (const item of generator) {
        yield transform(item);
      }
    };
    return new Enumerable(gen);
  }

  toArray() {
    return Array.from(this.generator());
  }
}

Пост заполнен ссылками, и для того, что вы не понимаете из поста, я призываю вас искать и учиться.

Оригинал: “https://dev.to/costinmanda/why-all-articles-about-demystifying-js-array-methods-are-rubbish-2325”