Javascript цикл for
Содержание:
- Операторы break и continue
- Flowchart of while Loop
- Циклы .forEach
- Цикл «while»
- Добавление/удаление элементов
- Термины: «унарный», «бинарный», «операнд»
- Цикл do…while
- Как сделать Object и простые объекты итерируемыми
- Приоритет операторов
- Цикл «for…in»
- || (ИЛИ)
- Классический цикл for
- JavaScript
- Присваивание
- Короткий цикл вычислений
- || (ИЛИ)
- Symbol.iterator
- Цикл for…in
- Async/Await и генераторы
- Использование for без блоков
- Заключение
Операторы break и continue
Циклом можно управлять с помощью операторов break и continue.
Оператор break приводит к выходу из цикла или инструкции и передает управление
операторам, следующим за ними.
В следующем примере создаётся счётчик, значения которого должны изменяться от до , однако оператор break прерывает цикл после итераций:
Выполнить код »
Скрыть результаты
Для вложенных циклов оператор используется с меткой, с помощью которой завершается работа «меченой» инструкции. Метка позволяет выйти из любого блока кода. Метка имеет вид , имя должно быть уникальным. Она ставится перед циклом или блоком инструкций, которые нужно завершить с помощью :
Выполнить код »
Скрыть результаты
Указание имени метки (без двоеточия) за ключевым словом приводит к выходу из цикла или инструкции. Между ключевым словом и именем метки не допускается перевод строки. Вызов завершит вложенный цикл, а ищет ближайший внешний цикл с такой меткой и переходит в его конец.
Оператор continue прерывает текущее выполнение цикла и переходит к выполнению следующего шага этого цикла. При этом, цикл возвращается непосредственно к своему условию, а цикл сначала вычисляет выражение инкремента, а затем возвращается к условию.
Рассмотрим пример:
Выполнить код »
Скрыть результаты
В теле цикла инструкция с помощью оператора проверяет, является ли число четным. Если да, итерация цикла завершается до увеличения переменной , но цикл продолжается со следующей итерации со значением , увеличенным на единицу. Затем цикл выполняется до естественного завершения при значении , равном 10. Переменная подсчитывает количество итераций цикла. Окончательное значение равно 5, а не 9, потому что четные операции инкремента пропускаются из-за оператора .
Оператор , как и , можно использовать вместе с «помеченными» инструкциями для
возврата к конкретному месту в коде. Чаще всего это делается во вложенных циклах, например:
Выполнить код »
Скрыть результаты
В этом примере для внешнего цикла добавлена метка outer_mask. Каждый цикл включает 5 итераций, то есть инструкция предположительно должна быть выполнена 25 раз, после чего переменная должна быть равна 25. Оператор завершает выполнение внутреннего цикла, начиная новую итерацию внешнего. Она выполняется, когда равно 3, то есть пропускаются две итерации внутреннего цикла, из-за чего в итоге имеет значение 23.
Flowchart of while Loop
Flowchart of JavaScript while loop
Example 1: Display Numbers from 1 to 5
Output
1 2 3 4 5
Here is how this program works.
Iteration | Variable | Condition: i <= n | Action |
---|---|---|---|
1st | 1 is printed. i is increased to 2. | ||
2nd | 2 is printed. i is increased to 3. | ||
3rd | 3 is printed. i is increased to 4. | ||
4th | 4 is printed. i is increased to 5. | ||
5th | 5 is printed. i is increased to 6. | ||
6th | The loop is terminated |
Example 2: Sum of Positive Numbers Only
Output
Enter a number: 2 Enter a number: 5 Enter a number: 7 Enter a number: 0 Enter a number: -3 The sum is 14.
In the above program, the user is prompted to enter a number.
Here, is used because takes input from the user as a string. And when numeric strings are added, it behaves as a string. For example, . So converts a numeric string to number.
The loop continues until the user enters a negative number. During each iteration, the number entered by the user is added to the variable.
When the user enters a negative number, the loop terminates. Finally, the total sum is displayed.
Циклы .forEach
Мне очень нравится работать с этой конструкцией просто потому, что я большой поклонник декларативного синтаксиса и декларативного способа написания вообще. Вышеприведённые виды цикла отлично работают и имеют свои преимущества в определённых случаях, но они также очень императивны. Т.е. мы должны писать то, что должно произойти с данными, а не то, что мы хотим с ними сделать.
Итак, оставим философские размышления. Метод .forEach — это ещё один вид цикла for. Он является частью объекта массива и предназначен для получения функции и дополнительного, необязательного параметра для повторного определения контекста этой функции при её выполнении.
Для каждого элемента внутри массива функция будет выполнена, и получит три аргумента (да, всё правильно, три аргумента, а не один, как обычно). Вот эти аргументы:
- Элемент, который обрабатывается в текущий момент.
- Индекс элемента (это уже упрощает задачу, которую мы пытались решить с помощью цикла for…of).
- Сам обрабатываемый массив. На случай если вам нужно что-то с ним сделать.
Небольшой пример:
Быстро и просто. Посмотрите, как легко можно работать с атрибутами внутри функции. Ниже пример того, как можно использовать второй дополнительный параметр метода :
Перезаписывая контекст вызываемой функции , я могу изменить её поведение, не изменяя её код.
В завершении хочу показать, что этот метод тоже работает с асинхронным кодом. Вот пример:
Обратите внимание, что мне больше не нужен IIFE, потому что я объявляю колбэк как async
Цикл «while»
Цикл имеет следующий синтаксис:
Код из тела цикла выполняется, пока условие истинно.
Например, цикл ниже выводит , пока :
Одно выполнение тела цикла по-научному называется итерация. Цикл в примере выше совершает три итерации.
Если бы строка отсутствовала в примере выше, то цикл бы повторялся (в теории) вечно. На практике, конечно, браузер не позволит такому случиться, он предоставит пользователю возможность остановить «подвисший» скрипт, а JavaScript на стороне сервера придётся «убить» процесс.
Любое выражение или переменная может быть условием цикла, а не только сравнение: условие вычисляется и преобразуется в логическое значение.
Например, – более краткий вариант :
Фигурные скобки не требуются для тела цикла из одной строки
Если тело цикла состоит лишь из одной инструкции, мы можем опустить фигурные скобки :
Добавление/удаление элементов
Мы уже знаем методы, которые добавляют и удаляют элементы из начала или конца:
- – добавляет элементы в конец,
- – извлекает элемент из конца,
- – извлекает элемент из начала,
- – добавляет элементы в начало.
Есть и другие.
Как удалить элемент из массива?
Так как массивы – это объекты, то можно попробовать :
Вроде бы, элемент и был удалён, но при проверке оказывается, что массив всё ещё имеет 3 элемента .
Это нормально, потому что всё, что делает – это удаляет значение с данным ключом . Это нормально для объектов, но для массивов мы обычно хотим, чтобы оставшиеся элементы сдвинулись и заняли освободившееся место. Мы ждём, что массив станет короче.
Поэтому для этого нужно использовать специальные методы.
Метод arr.splice(str) – это универсальный «швейцарский нож» для работы с массивами. Умеет всё: добавлять, удалять и заменять элементы.
Его синтаксис:
Он начинает с позиции , удаляет элементов и вставляет на их место. Возвращает массив из удалённых элементов.
Этот метод проще всего понять, рассмотрев примеры.
Начнём с удаления:
Легко, правда? Начиная с позиции , он убрал элемент.
В следующем примере мы удалим 3 элемента и заменим их двумя другими.
Здесь видно, что возвращает массив из удалённых элементов:
Метод также может вставлять элементы без удаления, для этого достаточно установить в :
Отрицательные индексы разрешены
В этом и в других методах массива допускается использование отрицательного индекса. Он позволяет начать отсчёт элементов с конца, как тут:
Метод arr.slice намного проще, чем похожий на него .
Его синтаксис:
Он возвращает новый массив, в который копирует элементы, начиная с индекса и до (не включая ). Оба индекса и могут быть отрицательными. В таком случае отсчёт будет осуществляться с конца массива.
Это похоже на строковый метод , но вместо подстрок возвращает подмассивы.
Например:
Можно вызвать и вообще без аргументов: создаёт копию массива . Это часто используют, чтобы создать копию массива для дальнейших преобразований, которые не должны менять исходный массив.
Метод arr.concat создаёт новый массив, в который копирует данные из других массивов и дополнительные значения.
Его синтаксис:
Он принимает любое количество аргументов, которые могут быть как массивами, так и простыми значениями.
В результате мы получаем новый массив, включающий в себя элементы из , а также , и так далее…
Если аргумент – массив, то все его элементы копируются. Иначе скопируется сам аргумент.
Например:
Обычно он просто копирует элементы из массивов. Другие объекты, даже если они выглядят как массивы, добавляются как есть:
…Но если объект имеет специальное свойство , то он обрабатывается как массив: вместо него добавляются его числовые свойства.
Для корректной обработки в объекте должны быть числовые свойства и :
Термины: «унарный», «бинарный», «операнд»
Прежде, чем мы двинемся дальше, давайте разберёмся с терминологией.
-
Операнд – то, к чему применяется оператор. Например, в умножении есть два операнда: левый операнд равен , а правый операнд равен . Иногда их называют «аргументами» вместо «операндов».
-
Унарным называется оператор, который применяется к одному операнду. Например, оператор унарный минус меняет знак числа на противоположный:
-
Бинарным называется оператор, который применяется к двум операндам. Тот же минус существует и в бинарной форме:
Формально, в последних примерах мы говорим о двух разных операторах, использующих один символ: оператор отрицания (унарный оператор, который обращает знак) и оператор вычитания (бинарный оператор, который вычитает одно число из другого).
Цикл do…while
Инструкция do…while (англ. делай до тех пор, пока) отличается от цикла while тем, что в do…while сначала выполняется тело цикла, а затем проверяется условие продолжения цикла. Из-за такой особенности do…while называют циклом с постусловием. Таким образом, если условие do…while заведомо ложное, то хотя бы один раз блок операторов в теле цикла do…while всё равно выполнится.
Инструкция do…while представляет собой конструкцию из двух операторов, используемых совместно. Синтаксис этой конструкции следующий:
Пример do…while:
Выполнить код »
Скрыть результаты
Этот цикл продолжается, пока переменная i меньше 4. Она равна в начале цикла и увеличивается на 1 на каждой итерации.
Циклы с постусловием обычно используются, если тело цикла должно быть выполнено хотя бы один раз.
Как сделать Object и простые объекты итерируемыми
Простые объекты не являются итерируемыми, как и объекты из .
Однако этот момент можно обойти, добавив @@iterator к Object.prototype с пользовательским итератором.
Переменная содержит свойства объекта, полученного с помощью вызова . В функции next возвращается каждое значение из переменной properties и обновляется count, чтобы получить следующее значение из переменной properties, используя переменную count в качестве индекса. Когда count будет равен длине properties, устанавливаем значение true, чтобы остановить итерацию.
Тестирование с помощью Object:
Работает!!!
С простыми объектами:
Та-дам!!
Стоить добавить этот способ в качестве полифилла, чтобы использовать for..of с любыми объектами в приложении.
Использование for…of с классами ES6
Можно использовать for..of для итерации по списку данных в экземпляре класса.
Класс Profiles обладает свойством , которое содержит массив пользователей. Возможно, потребуется отобразить эти данные в приложении с помощью for…of. Пробуем:
Очевидно, for…of не сработает
Вот несколько правил, чтобы сделать итерируемым:
- Объект должен иметь свойство .
- Функция должна возвращать итератор.
- должен реализовывать функцию .
Свойство @@iterator определяется с помощью константы .
Запускаем:
Свойство profiles отображено.
Приоритет операторов
В том случае, если в выражении есть несколько операторов – порядок их выполнения определяется приоритетом, или, другими словами, существует определённый порядок выполнения операторов.
Из школы мы знаем, что умножение в выражении выполнится раньше сложения. Это как раз и есть «приоритет». Говорят, что умножение имеет более высокий приоритет, чем сложение.
Скобки важнее, чем приоритет, так что, если мы не удовлетворены порядком по умолчанию, мы можем использовать их, чтобы изменить приоритет. Например, написать .
В JavaScript много операторов. Каждый оператор имеет соответствующий номер приоритета. Тот, у кого это число больше, – выполнится раньше. Если приоритет одинаковый, то порядок выполнения – слева направо.
Отрывок из таблицы приоритетов (нет необходимости всё запоминать, обратите внимание, что приоритет унарных операторов выше, чем соответствующих бинарных):
Приоритет | Название | Обозначение |
---|---|---|
… | … | … |
17 | унарный плюс | |
17 | унарный минус | |
16 | возведение в степень | |
15 | умножение | |
15 | деление | |
13 | сложение | |
13 | вычитание | |
… | … | … |
3 | присваивание | |
… | … | … |
Так как «унарный плюс» имеет приоритет , который выше, чем у «сложения» (бинарный плюс), то в выражении сначала выполнятся унарные плюсы, а затем сложение.
Цикл «for…in»
Для перебора всех свойств объекта используется цикл . Этот цикл отличается от изученного ранее цикла .
Синтаксис:
К примеру, давайте выведем все свойства объекта :
Обратите внимание, что все конструкции «for» позволяют нам объявлять переменную внутри цикла, как, например, здесь. Кроме того, мы могли бы использовать другое имя переменной
Например, часто используется вариант
Кроме того, мы могли бы использовать другое имя переменной. Например, часто используется вариант .
Упорядочены ли свойства объекта? Другими словами, если мы будем в цикле перебирать все свойства объекта, получим ли мы их в том же порядке, в котором мы их добавляли? Можем ли мы на это рассчитывать?
Короткий ответ: свойства упорядочены особым образом: свойства с целочисленными ключами сортируются по возрастанию, остальные располагаются в порядке создания. Разберёмся подробнее.
В качестве примера рассмотрим объект с телефонными кодами:
Если мы делаем сайт для немецкой аудитории, то, вероятно, мы хотим, чтобы код был первым.
Но если мы запустим код, мы увидим совершенно другую картину:
- США (1) идёт первым
- затем Швейцария (41) и так далее.
Телефонные коды идут в порядке возрастания, потому что они являются целыми числами: .
Целочисленные свойства? Это что?
Термин «целочисленное свойство» означает строку, которая может быть преобразована в целое число и обратно без изменений.
То есть, – это целочисленное имя свойства, потому что если его преобразовать в целое число, а затем обратно в строку, то оно не изменится. А вот свойства или таковыми не являются:
…С другой стороны, если ключи не целочисленные, то они перебираются в порядке создания, например:
Таким образом, чтобы решить нашу проблему с телефонными кодами, мы можем схитрить, сделав коды не целочисленными свойствами. Добавления знака перед каждым кодом будет достаточно.
Пример:
Теперь код работает так, как мы задумывали.
|| (ИЛИ)
Оператор «ИЛИ» выглядит как двойной символ вертикальной черты:
Традиционно в программировании ИЛИ предназначено только для манипулирования булевыми значениями: в случае, если какой-либо из аргументов , он вернёт , в противоположной ситуации возвращается .
В JavaScript, как мы увидим далее, этот оператор работает несколько иным образом. Но давайте сперва посмотрим, что происходит с булевыми значениями.
Существует всего четыре возможные логические комбинации:
Как мы можем наблюдать, результат операций всегда равен , за исключением случая, когда оба аргумента .
Если значение не логического типа, то оно к нему приводится в целях вычислений.
Например, число будет воспринято как , а – как :
Обычно оператор используется в для проверки истинности любого из заданных условий.
К примеру:
Можно передать и больше условий:
Классический цикл for
Здесь всё ясно. Это классический цикл for, где вы определяете внутренний счётчик, задаёте условие прекращения цикла и изменение шага (обычно увеличивая или уменьшая счётчик).
Синтаксис:
Я уверен, что вы прекрасно знаете эту конструкцию. Вот типичный пример:
Этот код прекрасно работает, но конструкция такого вида цикла может быть более гибкой, чем в этом примере. Вот как можно представить себе эту конструкцию:
Т.е. вы можете создавать циклы с более чем одним счётчиком, например. Или выполнять код на каждом шаге, который может даже не влиять на счётчики. Приведу несколько примеров.
Вот полностью валидный цикл:
Можно пойти ещё дальше и выйти за рамки типичных случаев, как в примере выше:
Вы даже можете заменить среднее выражение для вызова функции, при этом не забывая, что возвращаемое значение функции будет приведено к булеву значению.
Что делать с асинхронным кодом внутри классического цикла? Благодаря новой фиче: async/await, всё решается просто:
Обратите внимание, как просто можно использовать цикл. Как будто нет никакой асинхронности
Благодаря async/await мы возвращаемся к зависимости от базовой конструкции, такой как цикл for, чтобы перебрать набор асинхронных инструкций.
Раньше мы использовали колбэки и промисы, чтобы достичь того же результата, поэтому логика была намного сложнее. Для ее упрощения появились специальные библиотеки, такие как async.js .
Кстати, небольшое примечание: цикл for в моём примере находится внутри IIFE просто потому, что инструкция await должна быть внутри асинхронной функции, чтобы не было проблем из-за Node.
JavaScript
JS Array
concat()
constructor
copyWithin()
entries()
every()
fill()
filter()
find()
findIndex()
forEach()
from()
includes()
indexOf()
isArray()
join()
keys()
length
lastIndexOf()
map()
pop()
prototype
push()
reduce()
reduceRight()
reverse()
shift()
slice()
some()
sort()
splice()
toString()
unshift()
valueOf()
JS Boolean
constructor
prototype
toString()
valueOf()
JS Classes
constructor()
extends
static
super
JS Date
constructor
getDate()
getDay()
getFullYear()
getHours()
getMilliseconds()
getMinutes()
getMonth()
getSeconds()
getTime()
getTimezoneOffset()
getUTCDate()
getUTCDay()
getUTCFullYear()
getUTCHours()
getUTCMilliseconds()
getUTCMinutes()
getUTCMonth()
getUTCSeconds()
now()
parse()
prototype
setDate()
setFullYear()
setHours()
setMilliseconds()
setMinutes()
setMonth()
setSeconds()
setTime()
setUTCDate()
setUTCFullYear()
setUTCHours()
setUTCMilliseconds()
setUTCMinutes()
setUTCMonth()
setUTCSeconds()
toDateString()
toISOString()
toJSON()
toLocaleDateString()
toLocaleTimeString()
toLocaleString()
toString()
toTimeString()
toUTCString()
UTC()
valueOf()
JS Error
name
message
JS Global
decodeURI()
decodeURIComponent()
encodeURI()
encodeURIComponent()
escape()
eval()
Infinity
isFinite()
isNaN()
NaN
Number()
parseFloat()
parseInt()
String()
undefined
unescape()
JS JSON
parse()
stringify()
JS Math
abs()
acos()
acosh()
asin()
asinh()
atan()
atan2()
atanh()
cbrt()
ceil()
clz32()
cos()
cosh()
E
exp()
expm1()
floor()
fround()
LN2
LN10
log()
log10()
log1p()
log2()
LOG2E
LOG10E
max()
min()
PI
pow()
random()
round()
sign()
sin()
sqrt()
SQRT1_2
SQRT2
tan()
tanh()
trunc()
JS Number
constructor
isFinite()
isInteger()
isNaN()
isSafeInteger()
MAX_VALUE
MIN_VALUE
NEGATIVE_INFINITY
NaN
POSITIVE_INFINITY
prototype
toExponential()
toFixed()
toLocaleString()
toPrecision()
toString()
valueOf()
JS OperatorsJS RegExp
constructor
compile()
exec()
g
global
i
ignoreCase
lastIndex
m
multiline
n+
n*
n?
n{X}
n{X,Y}
n{X,}
n$
^n
?=n
?!n
source
test()
toString()
(x|y)
.
\w
\W
\d
\D
\s
\S
\b
\B
\0
\n
\f
\r
\t
\v
\xxx
\xdd
\uxxxx
JS Statements
break
class
continue
debugger
do…while
for
for…in
for…of
function
if…else
return
switch
throw
try…catch
var
while
JS String
charAt()
charCodeAt()
concat()
constructor
endsWith()
fromCharCode()
includes()
indexOf()
lastIndexOf()
length
localeCompare()
match()
prototype
repeat()
replace()
search()
slice()
split()
startsWith()
substr()
substring()
toLocaleLowerCase()
toLocaleUpperCase()
toLowerCase()
toString()
toUpperCase()
trim()
valueOf()
Присваивание
Давайте отметим, что в таблице приоритетов также есть оператор присваивания . У него один из самых низких приоритетов: .
Именно поэтому, когда переменной что-либо присваивают, например, , то сначала выполнится арифметика, а уже затем произойдёт присваивание с сохранением результата в .
Тот факт, что является оператором, а не «магической» конструкцией языка, имеет интересные последствия.
Большинство операторов в JavaScript возвращают значение. Для некоторых это очевидно, например сложение или умножение . Но и оператор присваивания не является исключением.
Вызов записывает в и возвращает его.
Благодаря этому присваивание можно использовать как часть более сложного выражения:
В примере выше результатом будет значение, которое присваивается переменной (то есть ). Потом оно используется для дальнейших вычислений.
Забавное применение присваивания, не так ли? Нам нужно понимать, как это работает, потому что иногда это можно увидеть в JavaScript-библиотеках.
Однако писать самим в таком стиле не рекомендуется. Такие трюки не сделают ваш код более понятным или читабельным.
Рассмотрим ещё одну интересную возможность: цепочку присваиваний.
Такое присваивание работает справа налево. Сначала вычисляется самое правое выражение , и затем результат присваивается переменным слева: , и . В конце у всех переменных будет одно значение.
Опять-таки, чтобы код читался легче, лучше разделять подобные конструкции на несколько строчек:
Польза от такого стиля особенно ощущается при быстром просмотре кода.
Короткий цикл вычислений
JavaScript вычисляет несколько ИЛИ слева направо. При этом, чтобы экономить ресурсы, используется так называемый «короткий цикл вычисления».
Допустим, вычисляются несколько ИЛИ подряд: . Если первый аргумент – , то результат заведомо будет (хотя бы одно из значений – ), и остальные значения игнорируются.
Это особенно заметно, когда выражение, переданное в качестве второго аргумента, имеет сторонний эффект – например, присваивает переменную.
При запуске примера ниже присвоение не произойдёт:
…А в примере ниже первый аргумент – , так что ИЛИ попытается вычислить второй, запустив тем самым присваивание:
|| (ИЛИ)
Оператор ИЛИ выглядит как двойной символ вертикальной черты:
Логическое ИЛИ в классическом программировании работает следующим образом: «если хотя бы один из аргументов , то возвращает , иначе – «. В JavaScript, как мы увидим далее, это не совсем так, но для начала рассмотрим только логические значения.
Получается следующая «таблица результатов»:
Если значение не логического типа – то оно к нему приводится в целях вычислений. Например, число будет воспринято как , а – как :
Обычно оператор ИЛИ используется в , чтобы проверить, выполняется ли хотя бы одно из условий, например:
Можно передать и больше условий:
Symbol.iterator
Мы легко поймём принцип устройства перебираемых объектов, создав один из них.
Например, у нас есть объект. Это не массив, но он выглядит подходящим для .
Например, объект , который представляет собой диапазон чисел:
Чтобы сделать итерируемым (и позволить работать с ним), нам нужно добавить в объект метод с именем (специальный встроенный , созданный как раз для этого).
- Когда цикл запускается, он вызывает этот метод один раз (или выдаёт ошибку, если метод не найден). Этот метод должен вернуть итератор – объект с методом .
- Дальше работает только с этим возвращённым объектом.
- Когда хочет получить следующее значение, он вызывает метод этого объекта.
- Результат вызова должен иметь вид , где означает, что итерация закончена, в противном случае содержит очередное значение.
Вот полная реализация с пояснениями:
Обратите внимание на ключевую особенность итераторов: разделение ответственности
- У самого нет метода .
- Вместо этого другой объект, так называемый «итератор», создаётся вызовом , и именно его генерирует значения.
Таким образом, итератор отделён от самого итерируемого объекта.
Технически мы можем объединить их и использовать сам как итератор, чтобы упростить код.
Например, вот так:
Теперь возвращает сам объект : у него есть необходимый метод , и он запоминает текущее состояние итерации в . Короче? Да. И иногда такой способ тоже хорош.
Недостаток такого подхода в том, что теперь мы не можем использовать этот объект в двух параллельных циклах : у них будет общее текущее состояние итерации, потому что теперь существует лишь один итератор – сам объект. Но необходимость в двух циклах , выполняемых одновременно, возникает редко, даже при наличии асинхронных операций.
Бесконечные итераторы
Можно сделать бесконечный итератор. Например, будет бесконечным при . Или мы можем создать итерируемый объект, который генерирует бесконечную последовательность псевдослучайных чисел. Это бывает полезно.
Метод не имеет ограничений, он может возвращать всё новые и новые значения, это нормально.
Конечно же, цикл с таким итерируемым объектом будет бесконечным. Но мы всегда можем прервать его, используя .
Цикл for…in
for…in используется для итерирования перечисляемых («enumerable») свойств объекта. Он применим ко всем объектам что имеют такие свойства.
Перечисляемое свойство — это такое свойство объекта, для которого параметр Enumerable установлен в true.
Синтаксис for…in
let rectangle={ height:100, width:200 } for(let prop in rectangle){ console.log(`${prop}=${rectangle}`) } // result height=100 width=200
Стоит заметить что цикл for in также проходит по унаследованным свойствам, если они перечисляемые. Кроме того, порядок выполнения случайный.
Цикл for…in и объекты
Метод for…in предоставляет самый простой способ итерирования ключей и значений объекта, так как объекты не имеют доступа к методу forEach для массивов. Конечно есть методы Object.keys и Object.values которые дают ту же функциональность, но цикл for…of более гибок.
Цикл for…in и массивы
Для массивов ключом является индекс. Из этого следует что индексы — это перечисляемые свойства.
Пример for…in для массива
let users= for(let index in users){ console.log(`users=${users}`) } // result users=root users=test
Но все же, в большинстве случаев не рекомендуется использовать цикл for..in для работы с массивами, преимущественно из-за того, что порядок итерирования не гарантируется.
Использование for…in для строк
Каждый символ в строке имеет индекс. И подобно массивам, ети индексы являются перечисляемыми свойствами.
Пример for…in для строки
let str= for(let index in str){ console.log(`${str}`) } // result I love JavaScript
Async/Await и генераторы
Другой крайний случай с forEach() — это то, что он не совсем правильно работает с async/await или генераторами. Если ваш callback forEach() является синхронным, то это не имеет значения, но вы не сможете использовать await внутри callback forEach ():
async function run() { const arr = ; arr.forEach(el => { // SyntaxError await new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); }); }
Вы также не сможете использовать yield:
function* run() { const arr = ; arr.forEach(el => { // SyntaxError yield new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); }); }
Но приведенные выше примеры отлично работают с for/of:
async function asyncFn() { const arr = ; for (const el of arr) { await new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); } } function* generatorFn() { const arr = ; for (const el of arr) { yield new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); } }
Даже если вы пометите свой callback forEach() как async, вам будет сложно заставить асинхронный метод forEach() работать последовательно. Например, приведенный ниже скрипт будет печатать 0-9 в обратном порядке.
async function print(n) { // Wait 1 second before printing 0, 0.9 seconds before printing 1, etc. await new Promise(resolve => setTimeout(() => resolve(), 1000 - n * 100)); // Will usually print 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 but order is not strictly // guaranteed. console.log(n); } async function test() { .forEach(print); } test();
T
Вывод: если вы используете async/await или генераторы, помните, что forEach() является синтаксическим сахаром. Как сахар, его следует использовать экономно и не для всего.
Использование for без блоков
Все три выражения в заголовке цикла for являются необязательными.
Например, в блоке инициализации не требуется инициализировать переменные. Цикл в примере ниже полностью идентичен приведённому выше:
Выполнить код »
Скрыть результаты
Как и блок инициализации, блок выражения также не является обязательным. Если вы опускаете выражение, то вы должны обязательно разбить цикл в теле, чтобы не создавать бесконечный цикл:
Выполнить код »
Скрыть результаты
Можно опустить все три блока. Для избежания зацикливания используйте break для завершения цикла, а также изменяйте (увеличивайте или уменьшайте) переменную-счётчик, так чтобы условие для оператора break в какой-то момент сработало:
Выполнить код »
Скрыть результаты
Примечание: Любое из выражений в цикле for может отсутствовать, однако сами точки с запятой обязательно должны присутствовать, иначе будет синтаксическая ошибка.
Заключение
Как правило, for/of — это самый надежный способ перебора массива в JavaScript. Он более лаконичен, чем обычный цикл for, и не имеет такого количества граничных случаев, как for/in и forEach(). Основным недостатком for/of является то, что вам нужно проделать дополнительную работу для доступа к индексу массива (см. дополнение), и вы не можете строить цепочки кода, как вы можете это делать с помощью forEach(). Но если вы знаете все особенности forEach(), то во многих случаях его использование делает код более лаконичным.
Дополнение: Чтобы получить доступ к текущему индексу массива в цикле for/of, вы можете использовать функцию .
for (const of arr.entries()) { console.log(i, v); // Prints "0 a", "1 b", "2 c" }
Оригинал: For vs forEach() vs for/in vs for/of in JavaScript
Spread the love