Оригинальный пост: 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 Функция генератора это функция, которая генерирует значения по своему усмотрению, преимуществом в том, что ей не нужно держать все элементы в памяти (например, массив), и любая операция, которая требует выполнения значений, выполняется только на тех, которые запрашивались код.
Вот что делает вышеупомянутый код:
- Он создает перечисленную обертку по массиве (не выполняет операцию, просто назначения)
- он фильтрует, определяя функцию генератора Это возвращает только красноватые цвета (но не выполняет работу) и возвращает перечисленную обертку над функцией
- Он игнорирует элементы с предыдущих страниц, определяя функцию генератора, которая подсчитывает элементы и возвращает элементы только после указанного числа (опять же, без работы) и возвращает перечисляемую обертку над функцией
- Затем он берет страницу, полную элементов, останавливаясь сразу после этого, определяя функцию генератора, которая выполняет это (без работы) и возвращает перечисленную обертку над функцией
- Он преобразует цвета в выходных элементах, определяя функцию генератора, которая отражает существующие элементы и возвращает преобразованные значения (без работы) и возвращает перечисляемую обертку над функцией
- Он отражает функцию генератора в текущем перечислении и заполняет массив значениями (все операции выполняются здесь)
А вот поток для каждого элемента:
- .toarray перечисляет функцию генератора. Выберите
- . Выберите перечисление функции генератора .take
- .take перечисляет функцию генератора .skip
- .skip перечисляет функцию генератора. Где
- . где перечисляет функцию генератора, которая итерация на массиве цветов
- Первый цвет красный, который красный, так что. Где «дает» его, он проходит как следующий элемент на итерации
- Страница 0, скажем так, так что .skip нечего пропустить, она дает цвет
- .take по -прежнему есть предметы для размера страниц, давайте предположим 20, так что он дает цвет
- .Выбрать дает цвет, преобразованный для вывода
- .toarray выталкивает цвет в результате
- Перейти к 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”