Пример использования метода reduce для сокращения массива
Позвольте мне сделать смелое заявление: циклы часто бывают бесполезными и затрудняют чтение кода. Для итераций в массивах, поиска, сортировки элементов и других подобных действий вы можете использовать один из методов, приведенных ниже.
Несмотря на эффективность, большинство этих методов все еще малоизвестны и не очень популярны. Я проделаю для вас трудную работу и расскажу о самых полезных. Считайте эту статью своим путеводителем по методам массивов JavaScript.
Примечание: Прежде чем мы начнем, вам нужно узнать одну вещь: я с предубеждением отношусь к функциональному программированию. Чтобы избежать побочных эффектов, я стремлюсь применять методы, не изменяющие исходный массив напрямую. Я не говорю вам отказаться от изменения массива вообще, но стоит учитывать, что некоторые методы к этому приводят. В результате появляются побочные эффекты, нежелательные изменения и, как следствие, баги.
Изначально эта статья была опубликована на сайте thomlom.dev — там вы можете найти больше материалов по веб-разработке.
Основы
Есть четыре метода, о которых стоит знать, если вы работаете с массивами. Это
map
, filter
, reduce
и оператор spread
. Они эффективны и полезны.map
Вы будете часто пользоваться методом
map
. Вообще, каждый раз, когда вам нужно изменить элементы массива, рассматривайте этот вариант.Он принимает один параметр — функцию, которая вызывается на каждом элементе массива, а затем возвращает новый массив так, что никаких побочных эффектов быть не может.
const numbers = [1, 2, 3, 4]
const numbersPlusOne = numbers.map(n => n + 1) console.log(numbersPlusOne) // [2, 3, 4, 5]
Также вы можете создать новый массив, который хранит только одно определенное свойство объекта.
const allActivities = [
{ title: 'My activity', coordinates: [50.123, 3.291] },
{ title: 'Another activity', coordinates: [1.238, 4.292] }
]
const allCoordinates = allActivities.map(activity => activity.coordinates)
console.log(allCoordinates) // [[50.123, 3.291], [1.238, 4.292]]
Итак, запомните: когда вам нужно изменить массив, подумайте об использовании map.
filter
Название этого метода говорит само за себя: применяйте его, когда хотите отфильтровать массив.
Как и
map
, filter
принимает в качестве единственного параметра функцию, которая вызывается на каждом элементе массива. Эта функция должна вернуть булево значение: true
— если вы хотите сохранить элемент в массиве;false
— если не хотите сохранять его.
В итоге у вас будет правильный новый массив с элементами, которые вы хотели оставить.
Например, в массиве можно сохранить только нечетные цифры.
const numbers = [1, 2, 3, 4, 5, 6]
const oddNumbers = numbers.filter(n => n % 2 !== 0) console.log(oddNumbers) // [1, 3, 5]
Также можно использовать filter, чтобы убрать определенный элемент в массиве.
const participants = [
{ id: 'a3f47', username: 'john' },
{ id: 'fek28', username: 'mary' },
{ id: 'n3j44', username: 'sam' },
]
function removeParticipant(participants, id) {
return participants.filter(participant => participant.id !== id)
}
console.log(removeParticipant(participants, 'a3f47')) // [{ id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }];
reduce
На мой взгляд, этот метод — самый сложный для понимания. Но как только вы его освоите, у вас появится куча возможностей.
Обычно метод
reduce
берет массив значений и соединяет их в одно значение. Он принимает два параметра, функцию callback (которая является редуктором) и опциональное начальное значение (которое является первым элементом массива по умолчанию). Сам редуктор принимает четыре параметра:- аккумулятор, собирающий возвращенные значения в редуктор;
- текущее значение массива;
- текущий индекс;
- массив, для которого был вызван метод
reduce
.
В основном вы будете использовать только первые два параметра — аккумулятор и текущее значение.
Но давайте не будем сильно углубляться в теорию и рассмотрим самый распространенный пример применения
reduce
.const numbers = [37, 12, 28, 4, 9]
const total = numbers.reduce((total, n) => total + n) console.log(total) // 90
В первой итерации аккумулятор, являющийся суммой, принимает начальное значение 37. Возвращенное значение — 37 + n, где n = 12. Получаем 49.
Во время второй итерации аккумулятор равен 49, возвращенное значение — 49 + 28 = 77. И так далее.
Метод
reduce
настолько функциональный, что вы можете использовать его для построения множества методов массивов вроде map
или filter
.const map = (arr, fn) => {
return arr.reduce((mappedArr, element) => {
return [...mappedArr, fn(element)]
}, [])
}
console.log(map([1, 2, 3, 4], n => n + 1)) // [2, 3, 4, 5]
const filter = (arr, fn) => {
return arr.reduce((filteredArr, element) => {
return fn(element) ? [...filteredArr] : [...filteredArr, element]
}, [])
}
console.log(filter([1, 2, 3, 4, 5, 6], n => n % 2 === 0)) // [1, 3, 5]
Как правило, мы присваиваем методу
reduce
начальное значение []
— аккумулятор. Для map
мы запускаем функцию, результат которой добавляется в конец аккумулятора при помощи оператора spread (мы поговорим о нем ниже, не волнуйтесь). Для filter
проделываем практически то же самое, только функцию filter запускаем на элементе. Если она принимает значение true, мы возвращаем предыдущий массив. В противном случае добавляем элемент в конец массива. Давайте рассмотрим более сложный пример: сильно сократим массив
[1, 2, 3, [4, [[[5, [6, 7]]]], 8]]
до [1, 2, 3, 4, 5, 6, 7, 8]
.function flatDeep(arr) {
return arr.reduce((flattenArray, element) => {
return Array.isArray(element) ? [...flattenArray, ...flatDeep(element)] : [...flattenArray, element]
}, [])
}
console.log(flatDeep([1, 2, 3, [4, [[[5, [6, 7]]]], 8]])) // [1, 2, 3, 4, 5, 6, 7, 8]
Этот пример очень похож на
map
, за исключением того, что здесь мы используем рекурсию. Я не буду подробно останавливаться на рекурсии, потому что это выходит за пределы нашей темы, но если вы хотите узнать больше, зайдите на этот отличный ресурс.Оператор spread (ES2015)
Я согласен, это не метод. Однако оператор spread помогает достигать разных целей при работе с массивами. Вы можете применить его, чтобы расширить значения одного массива в другом, а затем сделать копию или связать несколько массивов вместе.
const numbers = [1, 2, 3]
const numbersCopy = [...numbers]
console.log(numbersCopy) // [1, 2, 3]
const otherNumbers = [4, 5, 6]
const numbersConcatenated = [...numbers, ...otherNumbers]
console.log(numbersConcatenated) // [1, 2, 3, 4, 5, 6]
Внимание: оператор spread делает поверхностную копию оригинального массива. Но что значит «поверхностную»?
Такая копия будет дублировать оригинальные элементы как можно меньше. Если у вас есть массив с цифрами, строками или булевыми значениями (примитивные типы), проблем не возникает и значения действительно дублируются. Однако с объектами и массивами дело обстоит по-другому: копируется только ссылка на оригинальное значение. Поэтому если вы сделаете поверхностную копию массива, включающего объект, и измените объект в скопированном массиве, в оригинальном он тоже будет изменен, потому что у них одинаковая ссылка.
const arr = ['foo', 42, { name: 'Thomas' }]
let copy = [...arr]
copy[0] = 'bar'
console.log(arr) // No mutations: ["foo", 42, { name: "Thomas" }]
console.log(copy) // ["bar", 42, { name: "Thomas" }]
copy[2].name = 'Hello'
console.log(arr) // /!\ MUTATION ["foo", 42, { name: "Hello" }]
console.log(copy) // ["bar", 42, { name: "Hello" }]
Итак, если вы хотите создать реальную копию массива, который содержит объект или массивы, можете воспользоваться функцией lodash вроде cloneDeep. Но не нужно считать себя обязанным это сделать. Ваша цель — узнать, как все устроено под капотом.
Полезные методы
Ниже вы найдете другие методы, о которых тоже полезно знать и которые могут пригодиться для решения таких проблем, как поиск элемента в массиве, изъятие части массива и многое другое.
includes (ES2015)
Вы использовали когда-нибудь
indexOf
, чтобы узнать, есть элемент в массиве или нет? Ужасный способ проверки, правда? К счастью, метод
includes
делает проверку за нас. Задайте параметр для includes, и он проведет поиск элемента по массиву. const sports = ['football', 'archery', 'judo']
const hasFootball = sports.includes('football')
console.log(hasFootball) // true
concat
Метод concat можно применять для слияния двух или более массивов.
const numbers = [1, 2, 3]
const otherNumbers = [4, 5, 6]
const numbersConcatenated = numbers.concat(otherNumbers)
console.log(numbersConcatenated) // [1, 2, 3, 4, 5, 6]
// You can merge as many arrays as you want
function concatAll(arr, ...arrays) {
return arr.concat(...arrays)
}
console.log(concatAll([1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12])) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
forEach
Если вы хотите выполнить действие для каждого элемента массива, можно использовать метод
forEach
. Он принимает функцию в качестве параметра, который, в свою очередь, тоже принимает три параметра: текущее значение, индекс и массив.const numbers = [1, 2, 3, 4, 5]
numbers.forEach(console.log)
// 1 0 [ 1, 2, 3 ]
// 2 1 [ 1, 2, 3 ]
// 3 2 [ 1, 2, 3 ]
indexOf
Этот метод используют, чтобы вернуть первый индекс, при котором элемент можно найти в массиве. Также с помощью
indexOf
часто проверяют наличие элемента в массиве. Честно говоря, сейчас я применяю его нечасто.const sports = ['football', 'archery', 'judo']
const judoIndex = sports.indexOf('judo')
console.log(judoIndex) // 2
find
Метод
find
похож на filter
. Вам необходимо предоставить ему функцию, которая тестирует каждый элемент массива. Однако find
прекращает тестировать элементы, как только находит тот, что прошел проверку. Это не filter
, который выполняет итерации по всему массиву независимо от обстоятельств.const users = [
{ id: 'af35', name: 'john' },
{ id: '6gbe', name: 'mary' },
{ id: '932j', name: 'gary' },
]
const user = users.find(user => user.id === '6gbe')
console.log(user) // { id: '6gbe', name: 'mary' }
Итак, используйте метод
filter
, когда хотите отфильтровать весь массив, а метод find
, когда уверены, что ищете уникальный элемент в массиве.findIndex
Этот метод практически такой же, как
find
, но он возвращает индекс первого найденного элемента вместо самого элемента.const users = [
{ id: 'af35', name: 'john' },
{ id: '6gbe', name: 'mary' },
{ id: '932j', name: 'gary' },
]
const user = users.findIndex(user => user.id === '6gbe')
console.log(user) // 1
Вам может показаться, что
findIndex
и indexOf
— это одно и тоже. Не совсем. Первым параметром indexOf
является примитивное значение (булево значение, номер, строка, неопределенное значение или символ), тогда как первый параметр findIndex
— функция обратного вызова.Поэтому, когда вам нужно найти индекс элемента в массиве примитивных значений, вы можете работать с
indexOf
. Если у вас больше сложных элементов, например объектов, используйте findIndex
.slice
Когда вам нужно взять часть массива или скопировать массив, вы можете обратиться к методу
slice
. Но будьте внимательны: как и оператор spread, slice
возвращает поверхностную копию.const numbers = [1, 2, 3, 4, 5]
const copy = numbers.slice()
В начале статьи я упомянул, что циклы часто бывают бесполезными. Давайте я покажу, как от них можно избавиться.
Предположим, вы хотите вернуть определенное количество сообщений чата из API и вам нужно, чтобы отображались только пять из них. Ниже приведены два подхода: один с циклами, другой с методом
slice
.// The "traditional way" to do it:
// Determine the number of messages to take and use a for loop
const nbMessages = messages.length < 5 ? messages.length : 5
let messagesToShow = []
for (let i = 0; i < nbMessages; i++) {
messagesToShow.push(posts[i])
}
// Even if "arr" has less than 5 elements,
// slice will return an entire shallow copy of the original array
const messagesToShow = messages.slice(0, 5)
some
Если вы хотите проверить, пройдет ли тест хотя бы один элемент массива, можно воспользоваться
some
. Как и map
, filter
или find
, метод some
принимает функцию обратного вызова в качестве единственного параметра, а затем возвращает значение true
, если по крайней мере один элемент проходит проверку, и значение false
, если нет.Также
some
подходит для работы с разрешениями.const users = [
{
id: 'fe34',
permissions: ['read', 'write'],
},
{
id: 'a198',
permissions: [],
},
{
id: '18aa',
permissions: ['delete', 'read', 'write'],
}
]
const hasDeletePermission = users.some(user =>
user.permissions.includes('delete')
)
console.log(hasDeletePermission) // true
every
Этот метод похож на
some
, за исключением того, что он проверяет, чтобы условию соответствовал каждый элемент (а не один).const users = [
{
id: 'fe34',
permissions: ['read', 'write'],
},
{
id: 'a198',
permissions: [],
},
{
id: '18aa',
permissions: ['delete', 'read', 'write'],
}
]
const hasAllReadPermission = users.every(user =>
user.permissions.includes('read')
)
console.log(hasAllReadPermission) // false
flat (ES2019)
Это совершенно новые методы в мире JavaScript. Обычно
flat
создает новый массив, соединяя все элементы вложенного массива. Он принимает один параметр — число, которое указывает, насколько сильно вы хотите уменьшить размерность массива.const numbers = [1, 2, [3, 4, [5, [6, 7]], [[[[8]]]]]]
const numbersflattenOnce = numbers.flat()
console.log(numbersflattenOnce) // [1, 2, 3, 4, Array[2], Array[1]]
const numbersflattenTwice = numbers.flat(2)
console.log(numbersflattenTwice) // [1, 2, 3, 4, 5, Array[2], Array[1]]
const numbersFlattenInfinity = numbers.flat(Infinity)
console.log(numbersFlattenInfinity) // [1, 2, 3, 4, 5, 6, 7, 8]
flatMap (ES2019)
Угадаете, что делает этот метод? Могу поспорить, вы поймете по одному его названию.
Сначала он запускает функцию mapping для каждого элемента, а затем сокращает массив за один раз. Проще простого!
const sentences = [
'This is a sentence',
'This is another sentence',
"I can't find any original phrases",
]
const allWords = sentences.flatMap(sentence => sentence.split(' '))
console.log(allWords) // ["This", "is", "a", "sentence", "This", "is", "another", "sentence", "I", "can't", "find", "any", "original", "phrases"]
В этом примере у вас много предложений в массиве и вы хотите получить все слова. Вместо того чтобы использовать метод
map
и разделить все предложения на слова, а затем сократить массив, вы можете сразу использовать flatMap
.Затем вы можете подсчитать количество слов с функцией
reduce
(это не относится к flatMap
, просто я хочу показать вам другой пример использования метода reduce
).const wordsCount = allWords.reduce((count, word) => {
count[word] = count[word] ? count[word] + 1 : 1
return count
}, {})
console.log(wordsCount) // { This: 2, is: 2, a: 1, sentence: 2, another: 1, I: 1, "can't": 1, find: 1, any: 1, original: 1, phrases: 1, }
Метод
flatMap
также часто используется в реактивном программировании. Пример вы можете посмотреть здесь.join
Если вам нужно создать строку, основанную на элементах массива, метод
join
— то, что вам нужно. Он позволяет создавать новую строку, соединяя все элементы массива, разделенные предоставленным разделителем.Например, с помощью
join
можно визуально отобразить всех участников деятельности.const participants = ['john', 'mary', 'gary']
const participantsFormatted = participants.join(', ')
console.log(participantsFormatted) // john, mary, gary
А это более реальный пример, где вы можете сначала отфильтровать участников и получить их имена.
const potentialParticipants = [
{ id: 'k38i', name: 'john', age: 17 },
{ id: 'baf3', name: 'mary', age: 13 },
{ id: 'a111', name: 'gary', age: 24 },
{ id: 'fx34', name: 'emma', age: 34 },
]
const participantsFormatted = potentialParticipants
.filter(user => user.age > 18)
.map(user => user.name)
.join(', ')
console.log(participantsFormatted) // gary, emma
from
Это статический метод, который создает новый массив из массивоподобного или итерируемого объекта, например строки. Он может пригодиться, когда вы работаете с объектной моделью документа.
const nodes = document.querySelectorAll('.todo-item') // this is an instance of NodeList
const todoItems = Array.from(nodes) // now, you can use map, filter, etc. as you're workin with an array!
Вы увидели, что мы использовали тип массива вместо экземпляра массива? Вот почему этот метод называется статическим.
Затем вы можете поразвлекаться с узлами, например зарегистрировать слушателей события на каждый из них при помощи метода
forEach
.todoItems.forEach(item => {
item.addEventListener('click', function() {
alert(`You clicked on ${item.innerHTML}`)
})
})
Методы, изменяющие массив, о которых стоит знать
Ниже приведены другие стандартные методы. Их отличие в том, что они изменяют оригинальный массив. В изменении нет ничего плохого, но стоит учитывать это при работе.
Если вы не хотите изменять оригинальный массив, работая с этими методами, сделайте его поверхностную или полную копию заранее.
const arr = [1, 2, 3, 4, 5]
const copy = [...arr] // or arr.slice()
sort
Да,
sort
изменяет оригинальный массив. Фактически он сортирует элементы массива на месте. Метод сортировки по умолчанию трансформирует все элементы в строки и сортирует их в алфавитном порядке.const names = ['john', 'mary', 'gary', 'anna']
names.sort()
console.log(names) // ['anna', 'gary', 'john', 'mary']
Будьте внимательны: если вы, например, перешли с языка Python, то метод
sort
при работе с массивом цифр не даст вам желаемого результата. const numbers = [23, 12, 17, 187, 3, 90]
numbers.sort()
console.log(numbers) // [12, 17, 187, 23, 3, 90]
Как же тогда отсортировать массив? Метод
sort
принимает одну функцию — функцию сравнения. Она принимает два параметра: первый элемент (а
) и второй элемент для сравнения (b
). Сравнение между этими двумя элементами требует возврата цифры: - если значение отрицательное —
a
сортируется передb
; - если значение положительное —
b
сортируется передa
; - если значение равно 0 — без изменений.
Затем можно отсортировать цифры.
const numbers = [23, 12, 17, 187, 3, 90]
numbers.sort((a, b) => a - b)
console.log(numbers) // [3, 12, 17, 23, 90, 187]
Или можно отсортировать даты от наиболее поздней.
const posts = [
{
title: 'Create a Discord bot under 15 minutes',
date: new Date(2018, 11, 26),
},
{
title: 'How to get better at writing CSS',
date: new Date(2018, 06, 17) },
{
title: 'JavaScript arrays',
date: new Date()
},
]
posts.sort((a, b) => a.date - b.date) // Substracting two dates returns the difference in millisecond between them
console.log(posts)
// [ { title: 'How to get better at writing CSS',
// date: 2018-07-17T00:00:00.000Z },
// { title: 'Create a Discord bot under 15 minutes',
// date: 2018-12-26T00:00:00.000Z },
// { title: 'Learn Javascript arrays the functional way',
// date: 2019-03-16T10:31:00.208Z } ]
fill
Метод
fill
изменяет или заполняет все элементы массива от начального индекса до конечного заданным значением. Пример отличного использования fill
— заполнение нового массива начальными данными.// Normally I would have called a function that generates ids and random names but let's not bother with that here.
function fakeUser() {
return {
id: 'fe38',
name: 'thomas',
}
}
const posts = Array(3).fill(fakeUser())
console.log(posts) // [{ id: "fe38", name: "thomas" }, { id: "fe38", name: "thomas" }, { id: "fe38", name: "thomas" }]
reverse
Мне кажется, название метода полностью объясняет его суть.
const numbers = [1, 2, 3, 4, 5]
numbers.reverse()
console.log(numbers) // [5, 4, 3, 2, 1]
pop
Этот метод убирает последний элемент из массива и возвращает его.
const messages = ['Hello', 'Hey', 'How are you?', "I'm fine"]
const lastMessage = messages.pop()
console.log(messages) // ['Hello', 'Hey', 'How are you?']
console.log(lastMessage) // I'm fine
Методы, которые можно заменить
В последнем разделе вы найдете методы, которые изменяют оригинальный массив и которым легко найти альтернативу. Я не утверждаю, что их нужно сбрасывать со счетов, просто хочу донести до вас, что у некоторых методов есть побочные эффекты и их можно заменить.
push
Этот метод используется часто. Он позволяет добавлять один или более элементов в массив, а также строить новый массив, основанный на предыдущем.
const todoItems = [1, 2, 3, 4, 5]
const itemsIncremented = []
for (let i = 0; i < items.length; i++) {
itemsIncremented.push(items[i] + 1)
}
console.log(itemsIncremented) // [2, 3, 4, 5, 6]
const todos = ['Write an article', 'Proofreading']
todos.push('Publish the article')
console.log(todos) // ['Write an article', 'Proofreading', 'Publish the article']
Если вам нужно построить массив на основе другого, как в методе
itemsIncremented
, есть подходящие для этого и уже знакомые нам map
, filter
или reduce
. Например, мы можем взять map
, чтобы сделать это.const itemsIncremented = todoItems.map(x => x + 1)
А если вы хотите использовать
push
, когда нужно добавить новый элемент, то пригодится оператор spread.const todos = ['Write an article', 'Proofreading'] console.log([...todos, 'Publish the article'])
splice
К
splice
часто обращаются, чтобы убрать элемент на определенном индексе. Вы можете сделать то же самое с методом filter
.const months = ['January', 'February', 'March', 'April', ' May']
// With splice
months.splice(2, 1) // remove one element at index 2
console.log(months) // ['January', 'February', 'April', 'May']
// Without splice
const monthsFiltered = months.filter((month, i) => i !== 3)
console.log(monthsFiltered) // ['January', 'February', 'April', 'May']
Вы спросите: а если мне нужно убрать много элементов? Тогда используйте
slice
.
const months = ['January', 'February', 'March', 'April', ' May']
// With splice
months.splice(1, 3) // remove thirds element starting at index 1
console.log(months) // ['January', 'February', 'April', 'May']
// Without splice
const monthsFiltered = [...months.slice(0, 1), ...months.slice(4)]
console.log(monthsFiltered) // ['January', 'February', 'April', 'May']
shift
Метод
shift
убирает первый элемент массива и возвращает его. Чтобы сделать это в стиле функционального программирования, можно использовать оператор spread или rest.const numbers = [1, 2, 3, 4, 5]
// With shift
const firstNumber = numbers.shift()
console.log(firstNumber) // 1
console.log(numbers) // [2, 3, 4, 5]
// Without shift
const [firstNumber, ...numbersWithoutOne] = numbers
console.log(firstNumber) // 1
console.log(numbersWithoutOne) // [2, 3, 4, 5]
unshift
Метод unshift позволяет добавлять один или более элементов в начало массива. Как и в
shift
, вы можете делать это с помощью оператора spread.const numbers = [3, 4, 5]
// With unshift
numbers.unshift(1, 2)
console.log(numbers) // [1, 2, 3, 4, 5]
// Without unshift
const newNumbers = [1, 2, ...numbers]
console.log(newNumbers) // [1, 2, 3, 4, 5]
TL;DR
- Когда вы хотите совершить какие-то операции с массивом, не используйте цикл for и не изобретайте велосипед, потому что, скорее всего, найдется метод из вышеперечисленных, который может сделать то, что вам нужно.
- Чаще всего вы будете пользоваться методами
map
,filter
,reduce
и оператором spread — это важные инструменты для любого разработчика. - Существует также много методов массивов, которые хорошо бы знать:
slice
,some
,flatMap
, и т. д. Знакомьтесь с ними и применяйте при необходимости. - Побочные эффекты могут привести к нежелательным изменениям. Учитывайте, что некоторые методы изменяют ваш оригинальный массив.
- Метод
slice
и оператор spread делают поверхностные копии. В результате объекты и подмассивы будут иметь одинаковые ссылки — это тоже стоит иметь в виду. - Старые методы, изменяющие массив, можно заменить новыми. Вы сами решаете, как поступать.
Теперь вы знаете все, что должны были знать о массивах JavaScript. Если вам понравилась эта статья, нажмите на кнопку «Похлопать» (до 50 раз, если захотите :-)) и поделитесь ей. И не стесняйтесь обмениваться впечатлениями в комментариях!
Комментарии (13)
Aingis
05.04.2019 17:21+6М-да, без недосказанностей и ошибок не обошлось.
- Array.from может принимать функцию маппинга вторым аргументом.
А ещё — внезапно! —Array.from(new Set(['a', 'b']), x => x + x) // [ 'aa', 'bb' ]
querySelectorAll()
возвращает iterable-коллекцию.forEach()
по ней можно делать и так (кроме IE кажется, впрочем, так и так нужен будет полифилл). - fill имеет подвох: если заполнять массив объектом, это будет один и тот же объект. Если нужно чтобы были разные, лучше воспользоваться
Array.from()
с маппингом:Array.from({length: 3}, () => ({}))
- Второй аргумент splice как раз указывает сколько элементов надо убрать из массива. А третьим и следующими аргументами можно передать что вставить на вместо убранных элементов (можно и просто вставить:
[1, 3].splice(1, 0, 2)
преобразует исходный массив в[1, 2, 3]
, вернёт пустой массив). slice в отличие от splice не изменяет исходный массив. - Спреды в отличие от методов shift и unshift тоже не меняют исходный массив, а создают новый.
- Array.from может принимать функцию маппинга вторым аргументом.
xPomaHx
05.04.2019 19:12+2не используйте цикл for
Не первый раз слышу, может кто нибудь разъяснить почему?
я так наоборот не вижу смысла использовать метод forEach, кроме случаев когда у нас много переборов и колбэк можно куда то сохранить и вынести.
у обычного фора:
меньше скобок
он быстрее по времени
он дешевле по памяти
можно перебирать не всё а через раз
не до конца
в обратном порядкеSirion
05.04.2019 19:37+2Использование методов массивов лучше выражает семантику кода, делает его более самодокументированным. Уместное использование правильных методов, необходимо уточнить.
ilyapirogov
05.04.2019 19:52+2Лично для меня код с
for
:
for (const el of elements) { // ... }
Куда более выразительный и самодокументируемый, чем:
elements.forEach(el => { // ... })
Sirion
05.04.2019 23:08+2for-of — да, несомненно, это крутая штука. С его появлением forEach стал особо не нужен. Я говорил скорее об остальных методах. Когда for-of используется и вместо map, и вместо reduce, и вместо всего — это уже значительно менее идиоматично.
Aingis
05.04.2019 19:49-1for
попросту громоздкий по сравнению со специальными методами, которые в простых случаях можно даже в одну строку написать и будут лучше читаться. Но иногда без него не обойтись. Хотя да, новыйfor..of
даже покорочеforEach
будет (и возможноreduce
).
В принципе движки уже научились оптимизировать итерационные методы, так что на скорость вне совсем горячих мест можно не оглядываться.Borro
05.04.2019 21:00+1Скажите, какой движок научился оптимизировать forEach?
Вот тест, который говорит, что forEach в 10 раз медленнее for и в 5 раз медленнее for…of. Выигрывает он только у for…in, и то быстрее всего в 2 раза.
https://jsperf.com/for-vs-forin-vs-forof-vs-foreach
Скрин тестаAN3333
06.04.2019 09:01-1Не использование for вносит не нужное разнообразие и, соответственно, затрудняет чтение. Вот и весь эффект.
dimoff66
06.04.2019 09:17Это из области религии, тут нет однозначных объяснений, просто члены одной секты понимают собратьев без объяснений.
AN3333
06.04.2019 09:35For это не секта, это классика. Ничем себя не скомпрометировавшая.
А вот теперь пошел сленг. Точно такой же как и везде. Образуются молодежные группы. Вносятся искажения в язык. Раньше это было в естественном языке, теперь добралось до программирования. Это то же явление что деревня против деревни, пацаны нашего двора, против соседского.
Mox
06.04.2019 11:18Я все равно в итоге пришлось использовать lodash, очень он удобный. А раз его использую — то и все эти map, filter — тоже оттуда, для унификации кода. Возможно,
Sirion
Ммм… А как насчёт производительности?