Глубокое понимание цикла для цикла в JavaScript
источник:www.codeceo.comВ ECMAScript5 (сокращенно ES5) существует три типа циклов for, а именно:
В ECMAScript6 (сокращенно ES6), выпущенном в июне 2015 года, был добавлен новый цикл, а именно:
Давайте взглянем на эти 4 типа циклов for.
простой цикл
Рассмотрим наиболее распространенный способ написания:
const arr = [1, 2, 3];
for(let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
Когда длина массива не меняется во время цикла, мы должны сохранить длину массива в переменной, что позволит добиться большей эффективности.Следующий улучшенный способ записи:
const arr = [1, 2, 3];
for(let i = 0, len = arr.length; i < len; i++) {
console.log(arr[i]);
}
for-in
Обычно мы можем использовать for-in для обхода содержимого массива, код выглядит следующим образом:
const arr = [1, 2, 3];
let index;
for(index in arr) {
console.log("arr[" + index + "] = " + arr[index]);
}
В целом результаты операции таковы:
arr[0] = 1
arr[1] = 2
arr[2] = 3
Но это часто создает проблемы.
Правда о for-in
Цикл for-in перебирает свойства объекта, а не индексы массива. Следовательно, объекты, через которые проходит for-in, не ограничиваются массивами, но также могут проходить через объекты. Примеры следующие:
const person = {
fname: "san",
lname: "zhang",
age: 99
};
let info;
for(info in person) {
console.log("person[" + info + "] = " + person[info]);
}
Результат выглядит следующим образом:
person[fname] = san
person[lname] = zhang
person[age] = 99
Note that, for-in the order of traversal attribute is not determined, ie, the output result of the order of the attribute in the order of the objects is independent, also independent of the alphabetical order attribute, it is also independent of any other заказ.
Вся правда о массивах
Массив в Javascript — это объект, а индекс массива — это имя свойства. На самом деле, «массив» в Javascript несколько вводит в заблуждение. Массивы в Javascript не похожи на массивы в большинстве других языков. Во-первых, массивы в Javascript не являются непрерывными в памяти, а во-вторых, индексы массива не относятся к смещениям. На самом деле индекс массива не типа Number, а типа String. Причина, по которой мы можем правильно использовать такие вещи, как arr[0], заключается в том, что язык автоматически преобразует числа типа Number 0 преобразуется в «0» типа String. Итак, в Javascript никогда не бывает индекса массива, только такие свойства, как «0», «1» и т. д. Интересно, что каждый объект Array имеет свойство длины, благодаря чему он ведет себя больше как массивы в других языках. Но почему свойство length не выводится при обходе объекта Array? Это связано с тем, что for-in может проходить только по «перечисляемым свойствам», длина — это неперечисляемое свойство, фактически объекты Array имеют много других неперечисляемых свойств.
Теперь вернемся назад и посмотрим на пример использования for-in для прохода по массиву.Давайте изменим предыдущий пример обхода массива:
const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
console.log("arr[" + index + "] = " + arr[index]);
}
Результат бега таков:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello world
Мы видим, что for-in перебирает наше недавно добавленное свойство «name», потому что for-in перебирает все свойства объекта, а не только «index». При этом следует отметить, что здесь выводятся значения индекса, то есть "0", "1", "2" не типа Number, а типа String, потому что они выводятся как атрибуты, не индексы. Означает ли это, что без добавления новых свойств к нашему объекту Array мы можем просто вывести содержимое массива? ответ отрицательный. Поскольку for-in не только просматривает свойства самого массива, но также просматривает все перечисляемые свойства в цепочке прототипов массива. Давайте посмотрим на пример:
Array.prototype.fatherName = "Father";
const arr = [1, 2, 3];
arr.name = "Hello world";
let index;
for(index in arr) {
console.log("arr[" + index + "] = " + arr[index]);
}
Результат бега таков:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[name] = Hello world
arr[fatherName] = Father
Написав здесь, мы можем обнаружить, что for-in не подходит для обхода элементов в массиве, он больше подходит для обхода свойств в объекте, что также является первоначальным намерением его создания. Единственным исключением являются разреженные массивы. Рассмотрим следующий пример:
let key;
const arr = [];
arr[0] = "a";
arr[100] = "b";
arr[10000] = "c";
for(key in arr) {
if(arr.hasOwnProperty(key) &&
/^0$|^[1-9]\d*$/.test(key) &&
key <= 4294967294
) {
console.log(arr[key]);
}
}
for-in выполняет обход только существующих объектов.В приведенном выше примере for-in проходит 3 раза (обход элементов с атрибутами «0», «100» и «10000» соответственно, а обычный цикл for будет проходить 10001 раз). Таким образом, при правильном обращении for-in также может сыграть огромную роль в переборе элементов в массиве.
Чтобы избежать дублирования усилий, мы можем обернуть приведенный выше код:
function arrayHasOwnIndex(array, prop) {
return array.hasOwnProperty(prop) &&
/^0$|^[1-9]\d*$/.test(prop) &&
prop <= 4294967294; // 2^32 - 2
}
Пример использования следующий:
for (let key in arr) {
if (arrayHasOwnIndex(arr, key)) {
console.log(arr[key]);
}
}
for-in производительность
Как упоминалось выше, каждая итерация ищет свойства экземпляра или прототипа одновременно, и каждая итерация цикла for-in требует больше накладных расходов, поэтому он медленнее, чем другие типы циклов, обычно в 1/7 раза быстрее других типов. петель. Поэтому избегайте использования циклов for-in, если вам явно не нужно перебирать объект с неизвестным количеством свойств. Если вам нужно выполнить итерацию по ограниченному списку известных свойств, будет быстрее использовать другие циклы, такие как следующий пример:
const obj = {
"prop1": "value1",
"prop2": "value2"
};
const props = ["prop1", "prop2"];
for(let i = 0; i < props.length; i++) {
console.log(obj[props[i]]);
}
В приведенном выше коде атрибуты объекта хранятся в массиве, и по сравнению с for-in для поиска каждого атрибута код фокусируется только на данном атрибуте, что экономит накладные расходы и время цикла.
forEach
В ES5 появился новый цикл forEach.
const arr = [1, 2, 3];
arr.forEach((data) => {
console.log(data);
});
результат операции:
1
2
3
Метод forEach выполняет функцию обратного вызова один раз для каждого элемента в массиве, который имеет допустимое значение, и те элементы, которые были удалены (с помощью метода удаления и т. д.) или которым никогда не присваивалось значение, будут пропущены (за исключением тех, чье значение неопределенный или нулевой). В callback-функцию поочередно передаются три параметра:
- значение текущего элемента массива;
- индекс текущего элемента в массиве;
- сам объект массива;
Следует отметить, что область обхода forEach определяется до первого вызова обратного вызова. Элементы, добавленные в массив после вызова forEach, не будут доступны для обратного вызова. Если существующие значения изменены, значение, переданное в callback, является значением forEach в момент перехода к ним. Удаленные элементы не просматриваются.
const arr = [];
arr[0] = "a";
arr[3] = "b";
arr[10] = "c";
arr.name = "Hello world";
arr.forEach((data, index, array) => {
console.log(data, index, array);
});
результат операции:
a 0 ["a", 3: "b", 10: "c", name: "Hello world"]
b 3 ["a", 3: "b", 10: "c", name: "Hello world"]
c 10 ["a", 3: "b", 10: "c", name: "Hello world"]
Здесь index имеет тип Number и не пересекает свойства в цепочке прототипов, как это делает for-in.
Итак, при использовании forEach нам не нужно специально объявлять индекс и элементы для обхода, потому что они используются в качестве параметров функции обратного вызова.
Кроме того, forEach будет перебирать все элементы массива, но ES5 определяет некоторые другие полезные методы, вот некоторые из них:
- каждый: цикл возвращается после первого возврата false
- some: цикл возвращается после первого возврата true
- фильтр: возвращает новый массив, элементы которого удовлетворяют функции обратного вызова
- карта: обработайте элементы в исходном массиве, а затем верните
- уменьшить: элементы в массиве обрабатываются последовательно, результат последней обработки используется в качестве входных данных для следующей обработки, и, наконец, получается окончательный результат.
для каждого выступления
Первая благодарность@papa paНапоминание, только чтобы понять, что мое предыдущее понимание было неправильным.
каждый может видетьjsPerf, результаты тестирования в разных браузерах таковы, что forEach работает не так быстро, как for. Если вы поместите тестовый код на консоль, вы можете получить разные результаты, основная причина в том, что среда выполнения консоли отличается от среды выполнения реального кода.
for-of
Сначала рассмотрим пример:
const arr = ['a', 'b', 'c'];
for(let data of arr) {
console.log(data);
}
Результат бега таков:
a
b
c
Зачем вводить for-of?
Чтобы ответить на этот вопрос, давайте взглянем на недостатки трех типов циклов for до ES6:
- forEach не может сломаться и вернуться;
- Недостаток for-in более очевиден: он не только обходит элементы в массиве, но и обходит пользовательские свойства, и даже осуществляется доступ к свойствам в цепочке прототипов. Кроме того, порядок обхода элементов массива может быть случайным.
Итак, ввиду вышеперечисленных недостатков, нам необходимо улучшить исходный цикл for. Но ES6 не сломает ваш уже написанный код JS. В настоящее время тысячи веб-сайтов полагаются на циклы for-in, а некоторые из них даже используют их для обхода массива. Добавление поддержки обхода массива путем исправления цикла for-in сделало бы это еще более запутанным, поэтому комитет по стандартам добавил новый синтаксис цикла в ES6 для решения текущей проблемы — for-of.
Итак, что может сделать for-of?
- По сравнению с forEach он может корректно реагировать на break, continue, return.
- Циклы for-of поддерживают не только массивы, но и большинство массивоподобных объектов, таких как объекты нодлиста DOM.
- Цикл for-of также поддерживает обход строки, который перебирает строку как последовательность символов Unicode.
- for-of также поддерживает обход объектов Map и Set (оба новые в ES6).
Подводя итог, циклы for-of имеют следующие характеристики:
- Это самый лаконичный и простой синтаксис для перебора элементов массива.
- Этот метод позволяет избежать всех ловушек циклов for-in.
- В отличие от forEach, он правильно реагирует на операторы break, continue и return.
- Он может проходить не только по массивам, но также по массивоподобным объектам и другим итерируемым объектам.
Однако следует отметить, что циклы for-of не поддерживают обычные объекты, но если вы хотите перебирать свойства объекта, вы можете использовать цикл for-in (что он и делает).
И последнее, но не менее важное: в ES6 есть еще один способ перебора значений массива, и это Iterator. Последний пример:
const arr = ['a', 'b', 'c'];
const iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
Тем не менее, это содержание выходит за рамки этой статьи, и Iterator есть о чем поговорить. У меня есть время, чтобы написать статью об этом в будущем. Добро пожаловать, чтобы обратить внимание.