Обзор технических различий между циклами в JavaScript
При работе с циклами в JavaScript необходимо правильно определить две вещи: enumerable properties
и iterable objects
.
Перечислимые свойства
Одной из определяющих характеристик перечисляемого объекта является то, что мы устанавливаем внутренний флаг перечисления в true, когда присваиваем свойство объекту с помощью оператора присваивания. Это значение по умолчанию.
Однако мы можем изменить это поведение, установив для него значение false.
Практическое правило заключается в том, что перечислимое свойство всегда отображается в цикле for … in
.
Давайте посмотрим на это в действии:
// отображается в цикле for .... in
const gbols = {};
gbols.platform = "LogRocket";
Object.getOwnPropertyDescriptor(gbols, "platform")
{value: "LogRocket", writable: true, enumerable: true, configurable: true}
// не отображается в цикле for .... in
// для большего контроля над этими свойствами мы используем
Object.defineProperty(gbols, 'role', {value: 'Admin', writable: true, enumerable: false})
// Тестирование результатов для
for (const item in gbols) {
console.log(item)
}
// регистрирует платформу
Итерируемые объекты
Объект является итеративным, если он определяет свое итерационное поведение. Значение, которое будет зациклено в конструкции for …of
, в этом случае будет определять ее итерационное поведение. Встроенные типы, которые являются итерационными, включают в себя Arrays
, Strings
, Sets
и Maps
в object
не является итерационным, так как он не указывает @iterator method
.
По сути, в Javascript все итерации являются перечисляемыми, но не все перечисляемые объекты являются итерациями.
Вот способ концептуализировать это: for …in
ищет объект в данных, в то время как for ..of
ищет повторяющиеся последовательности.
Посмотрим, как это все выглядит при использовании с типом данных Array
:
const authors = ['Jade', 'Dafe', 'Gbols', 'Daniel'];
// использование с циклом
for (const author in authors) {
console.log(author)
}
// записывает 0,1,2,3
for (const author of authors) {
console.log(author)
}
// регистрирует Jade, Dafe, Gbols, Daniel
При использовании этих конструкций следует помнить о том, что если вызывается typeof
, и вы получаете ответ object
, тогда вы можете использовать цикл for …in
.
Давайте посмотрим на эту операцию с авторской переменной:
typeof authors
// регистрирует "объект", поэтому мы можем использовать for ..in
Поначалу это может показаться удивительным, но важно отметить, что массивы - это особый вид объектов с индексами в качестве ключа. Знание того, что for ...in
будет искать объект в конструкции, может нам чрезвычайно помочь. Когда цикл for ...in
находит объект, он перебирает каждый ключ.
Мы можем визуализировать как for ..in
совершает процесс обхода авторских массивов следующим образом:
const authors = {
0: 'Jade',
1: 'Dafe',
2: 'Gbols',
3: 'Daniel'
}
Важное замечание: если его можно отследить до объекта (или унаследовать его от прототипной цепочки объекта), for …in
будет выполняться итерацию по ключу в произвольном порядке.
Между тем, если он реализует конструкцию итератора for.. of
, он будет перебирать значение на каждой итерации.
ForEach и map методы
Хотя forEach
и map
методы могут быть использована для достижения тех же вещей, есть различия в их поведении и особенности об их исполнении.
На базовом уровне они оба получают обратный вызов в качестве аргумента при вызове функции.
Рассмотрим следующий фрагмент:
const scoresEach = [2,4 ,8, 16, 32];
const scoresMap = [2,4 ,8, 16, 32];
const square = (num) => num * num;
Давайте перечислим несколько отличий в их работе.
forEach
возвращает undefined
, а map
возвращает новое array
:
let newScores = []
const resultWithEach = scoresEach.forEach((score) => {
const newScore = square(score);
newScores.push(newScore);
});
const resultWithMap = scoresMap.map(square);
console.log(resultWithEach) // записывает неопределенный
console.log(resultWithMap) // регистрирует [4, 16, 64, 256, 1024]
Map
является чистой функцией, но forEach
выполняет некоторую мутацию:
console.log(newScores) // logs [4, 16, 64, 256, 1024]
На мой взгляд, map
отдает предпочтение парадигме функционального программирования. Нам не всегда нужно выполнять мутацию, чтобы получить желаемый результат, в отличие от forEach
, где нам приходилось мутировать переменную newScores
. При каждом прогоне, когда предоставляется один и тот же ввод, функция map
будет давать один и тот же результат. Между тем, аналог forEach
возьмет предыдущее значение последней мутации.
Цепочка
Цепочка возможна с map
, так как возвращаемый результат - это array
. Следовательно, любой другой метод массива может быть немедленно вызван для результата. Другими словами, мы можем назвать filter
, reduce
, some
и т.д. Это не возможно с forEach
, а возвращаемое значение не определено.
Производительность
Метод map
, как правило, работает лучше, чем метод forEach
. Вы можете использовать JsPerf для проверки производительности эквивалентного блока кода, реализованного с помощью map
и forEach
. В среднем вы увидите, что функция map
работает как минимум на 50 процентов быстрее.
NB: Этот тест зависит от используемого вами компьютера, а также от реализации вашего браузера.
Вывод
Из всех описанных выше циклических конструкций наиболее управляемым является цикл for..of
. Мы можем использовать его с ключевыми словами return
, continue
и break
. Это означает, что мы можем указать, что мы хотим, чтобы произошло с каждым элементом в array
, независимо от того, хотим ли мы уйти пораньше или пропустить.
Помня об этой информации, убедитесь, что вы используете соответствующий инструмент, в зависимости от того, чего вы хотите достичь в своем коде.