Объекты, массивы и строки - for, forEach, for… of, for… in, while, do… while, every, some, find, findIndex, includes, map, filter, reduce

Вы когда-нибудь задумывались, какой цикл использовать при кодировании JavaScript? Иногда вы можете задаться вопросом, есть ли ярлык, который сэкономит ваше время, или ваш цикл вызовет ошибку.

Цель этой статьи - собрать всю важную информацию о циклах в одном месте, чтобы вы могли просто пролистать их и найти свой цикл.

Итак, без лишних слов, вот циклы JavaScript, которые вы можете использовать в своем коде, организованные в циклы для объектов, массивов и строк.

Цикл через объекты

1. для… в

for…in - это цикл JavaScript, который лучше всего подходит для объектов. Этот цикл перебирает все свойства объекта. Ключи свойств должны быть строками. Ключи, которые являются символами, пропускаются.

Свойства объекта должны быть перечислимыми, если вам нужно, чтобы они были видны циклу for…in. Перечислимые свойства - это просто свойства, у которых для внутреннего перечислимого флага установлено значение true. Для объектов, созданных с помощью литерала объекта или с помощью инициализатора свойства (new Object(), Object.create()), свойство enumerable по умолчанию будет иметь значение true.

Если вы не хотите, чтобы цикл for…in отображал определенное свойство, вы можете установить неперечислимое свойство следующим образом:

const myObject = {
    keyOne: 5,
    keyTwo: "valueTwo",
    differentkey: "value3"
};
Object.defineProperty(myObject, 'newKey', {
  value: "can't see me in a for...in loop",
  configurable: false, // default is false
  writable: false, // default is false
  enumerable: false, // default is false
});

Цикл for…in выполняет итерацию по объекту в случайном порядке, поэтому разработчики не должны полагаться на порядок, в котором свойства были добавлены к объекту.

При изменении / добавлении / удалении свойств объекта при повторении цикла важно учитывать следующее:

  • Любое удаленное свойство можно посетить до его удаления.
  • Любое измененное свойство можно посещать или не посещать до того, как оно будет изменено.
  • Добавленная собственность может быть или не может быть посещена.

Ключевые слова break и continue можно использовать в цикле for…in, но нет доступа к какому-либо индексу, как в цикле for.

Цикл for…in может перебирать свойства прототипа, поэтому рекомендуется выполнить hasOwnProperty() проверку. (см. ниже: для… из vs для… в)

const myObject = {
    keyOne: 5,
    keyTwo: "valueTwo",
    differentkey: "value3"
};
for (var keyName in myObject) {
  if (myObject[keyName] > 4 && myObject["differentkey"]) {
    // may not be deleted before loop reaches it
    delete myObject["differentkey"];
  }
  if (myObject.hasOwnProperty(keyName)) {
    console.log(keyName + " : " + myObject[keyName]);
  }
}

2. для… из

Поскольку for…of циклы не создаются для объектов, вы должны сначала сохранить ключи или значения вашего объекта или оба ключа и значения в массиве, используя методы Object.keys(), Object.values() или Object.entries() соответственно.

Вы можете использовать деструктуризацию, for…of и Object.values() / Object.keys() / Object.entries() вместе:

for (const [value] of Object.values(myObject)) {
  console.log(value);
}
for (const [key] of Object.keys(myObject)) {
  console.log(key);
}
for (const [key, value] of Object.entries(myObject)) {
  console.log(key, value);
  if (myObject[keyName] < 4) {
    break;
  }
}

break и continue работают внутри цикла for…of, но нет доступа к какому-либо индексу, как в цикле for.

Вышеупомянутый for…of цикл, вероятно, станет вашим основным методом обхода объектов.

3. forEach

Как в 2. выше, вы можете сохранить ключи или значения вашего объекта или и ключи и значения в массиве, используя методы Object.keys(), Object.values() или Object.entries() соответственно, а затем перебрать их с помощью цикла Array.prototype.forEach().

Object.keys(myObject).forEach(
    (key, index) => console.log(key, index)
);
Object.values(myObject).forEach(
    (value, index) => console.log(value, index)
);
Object.entries(myObject).forEach(
    ([key, value], index) => console.log(key, value, index)
);

4. для

Есть лучшие способы перебрать объект в цикле, но в редких случаях вы можете перебрать объект с помощью цикла for. Преимущество этого метода в том, что у вас есть доступ к индексу ключей объекта (у объектов нет индексов как таковых, но вы можете сохранить ключи в массиве с помощью Object.keys()).

Порядок свойств объекта в JavaScript сложный, поэтому не полагайтесь на порядок массива, сгенерированного с использованием Object.keys(), чтобы он был согласованным или отражал порядок вставки в ваш объект. Однако вы можете надежно делать такие вещи, как доступ только к каждому второму свойству, используя i%2.

Ниже цикл начинается с i = 100 и прерывается при i = 155 (56 ключей). Если у вас есть объект с 2000 ключами, вы можете сэкономить время, ограничив цикл интересующими вас значениями индекса.

Чтобы получить верхнюю границу цикла, используйте метод Object.keys() для записи ключей объекта в массив и получения длины этого массива.

const myObjectKeys = Object.keys(myObject);
for(let i = 100; index < myObjectKeys.length; index++) {
  if (myObjectKeys[i] === "thisKey") {
    keyInThisRange = true;
  }
  arrayOfAllRangeValues.push(myObject[myObjectKeys[i]]);
  if (i%2 == true) {
    arrayOfEverySecondValue.push(myObject[myObjectKeys[i]]);
  }
  if (i > 155) break;
}

Цикл через массивы

для… из

Это цикл по умолчанию, используемый для массивов. Цикл for…of выполняет итерацию по итерируемым объектам. Это не те же объекты, которые созданы с помощью литерала объекта или с помощью new Object() или Object.create(). Массивы являются одним из таких повторяемых объектов.

Синтаксис следующий:

const myArray = [3, 4, 19];
for (const value of myArray) {
  console.log(value);
}
// use let, not const, if reassigning values inside the loop
for (let value of myArray) {
  value += 20;
  console.log(value);
}

Ключевые слова break и continue можно использовать в цикле for…of, но нет доступа к какому-либо индексу, как в цикле for.

для каждого

Этот цикл предназначен исключительно для массивов. Функция принимает обратный вызов с тремя аргументами, вторые два, index и myArray, являются необязательными. index даст вам индекс текущего значения, а myArray - это массив, для которого вы вызвали метод. Вы можете использовать индекс, чтобы узнать, где вы находитесь, но вы не можете начать с определенного индекса, как с цикла for.

let myArray = [3, 4, 19];
myArray.forEach((value, index, myArray) => {
  console.log(value, index, myArray);
});

Важно отметить следующее:

  • forEach() петли не могут быть остановлены или прерваны с помощью break или continue.
  • forEach() ожидает, что обратный вызов будет синхронной функцией, он не будет ждать выполнения обещания. Следующее не сработает:
let myArray = [3, 4, 2];
let total = 0;

let addFunction = async (x) => {
  return x + 2
};

myArray.forEach(async (number) => {
  total = await addFunction(total)
});

console.log(total)
// Expected output with async function: 15
// Actual output: 0
  • Обратный вызов forEach() не будет посещать элементы, которые добавляются к массиву после вызова обратного вызова.

за

Цикл for хорошо работает с массивами.

let myArray = ["Newton", "Isaac", "Hello,"];
let myArrayLength = myArray.length;
for (let i = myArrayLength; i > 0 ; i--) {
    console.log(myArray[i]); //  "Hello,", "Isaac", "Newton",
}

Преимущество цикла for заключается в том, что вы полностью контролируете индекс. Например, вы можете начать цикл с конца, если хотите, как в примере выше.

для… в

for..in можно использовать для перебора массивов, но порядок посещения элементов не гарантируется, и по этой причине он никогда не используется с массивами. (см. также ниже: для… из vs для… в)

Следующие методы - это Array.prototype методы, которые вы можете использовать вместо ручного использования любого из традиционных циклов.

карта

map создаст новый массив путем обхода массива и выполнения функции для каждого элемента в этом массиве.

const myArray = [3, 6, 9, 12, 27];
// place your mapping function in the method
const myArrayMap = myArray.map(x => x / 3);
console.log(myArrayMap); // [1, 2, 3, 4, 9]

фильтр

filter создаст новый массив, перебирая массив и выполняя тестовую функцию для каждого элемента в этом массиве. Все элементы, прошедшие проверку, будут помещены в новый массив.

const myNumbersArray= [3, 6, 9, 12, 27];
const arrayOfNumbersGreaterThan6 = myNumbersArray.filter(number => number > 6);
console.log(arrayOfNumbersGreaterThan6); // [9, 12, 27]

уменьшать

reduce уменьшит ваш массив до одного значения, выполнив функцию для всех элементов.

const myArray = [2, 5, 3, 6];
const reducerFunction = (accumulatingValue, currentValue) => accumulatingValue + currentValue;
console.log(myArray.reduce(reducerFunction)); // 16
 
// initial value of 15
console.log(myArray.reduce(reducerFunction, 15)); // 31

Существует также метод зеркального отражения Array.prototype.reduceRight(), который начинается с правой стороны.

немного

some выполнит цикл по массиву и вернет логическое значение, указывающее, прошел ли хотя бы один из элементов вашу тестовую функцию.

const myNumbersArray= [3, 6, 9, 12, 27];
const aNumberGreaterThan12 = myNumbersArray.some(number => number > 12);
console.log(aNumberGreaterThan12); // true

каждый

every выполнит цикл по массиву и вернет логическое значение, указывающее, прошли ли все элементы вашу тестовую функцию.

const myNumbersArray= [3, 6, 9, 12, 27];
const allNumbersGreaterThan2 = myNumbersArray.every(number => number > 2);
console.log(allNumbersGreaterThan2); // true

включает

Это очень полезный цикл. Чтобы проверить, имеет ли массив определенное значение, используйте Array.prototype.includes() для его проверки. Этот метод будет перебирать значения массива, чтобы проверить ваше значение, и вернет true или false.

Это не то же самое, что описанный ранее метод some. includes не принимает тестовую функцию, только значение.

const isItThere = myArray.includes("this string");

Если вы хотите только подтвердить наличие ключа или значения в объекте, нет необходимости вручную перебирать весь объект. Используйте метод Object.values() или Object.keys() для записи значений / ключей в новый массив, а затем используйте Array.includes() для проверки истинности или ложности.

const myObject = {
    keyOne: 5,
    keyTwo: "valueTwo",
    differentkey: "value3"
};
myObjectValuesArray = Object.values(myObject);
const helloValue = myObjectValuesArray.includes("hello"); // false
myObjectKeysArray = Object.keys(myObject);
const helloKey = myObjectKeysArray.includes("keyTwo"); // true

найти

find выполнит цикл по массиву и вернет значение первого элемента, прошедшего вашу тестовую функцию. Если элемент не найден, возвращается undefined.

const myNumbersArray= [3, 6, 9, 12, 27];
const valueOfFirstNumberGreaterThan6 = myNumbersArray.find(number => number > 6);
console.log(valueOfFirstNumberGreaterThan6); // 9

findIndex

findIndex выполнит цикл по массиву и вернет индекс первого элемента, прошедшего вашу тестовую функцию. Если элемент не найден, возвращается -1.

const myNumbersArray= [3, 6, 9, 12, 27];
const indexOfFirstNumberGreaterThan6 = myNumbersArray.findIndex(number => number > 6);
console.log(indexOfFirstNumberGreaterThan6); // 2

Сквозь струны

для… из

Если вам не нужно управлять индексом итерации, то лучшим вариантом будет цикл for…of.

const myString = "Hello";
for (let letter of myString) {
  console.log(letter); // "H", "e", "l", "l", "o"
}

за

Если вам нужен доступ к индексу, используйте for.

const myString = "Hello";
for (let i = 2; i < myString.length; i++) {
  console.log(i); // "H", "e", "l", "l", "o"
}

для каждого

Вы можете использовать forEach, если сначала разложите свою строку в массив, используя синтаксис распространения .

const myString = "Hello";
[...myString].forEach((letter) => {
   console.log(letter); // "H", "e", "l", "l", "o"
})

Другие общие петли

пока

Цикл while очень похож на цикл for без первого и третьего операторов. Значение индекса объявляется перед циклом, например let i = 0;, и индекс увеличивается внутри цикла на i++. Следующие циклы for и while эквивалентны:

// FOR LOOP
const myNumbersArray= [3, 6, 9, 12, 27];
let i = 0;
for (;myNumbersArray[i];) {
 console.log(myNumbersArray[i]);
 i++;
}
// WHILE LOOP
const myNumbersArray= [3, 6, 9, 12, 27];
let i = 0;
while (myNumbersArray[i]) {
 console.log(myNumbersArray[i]);
 i++;
}

Вы можете использовать while так же, как for:

const myObject = {
    keyOne: 5,
    keyTwo: "valueTwo",
    differentkey: "value3"
};
let i = 0;
const obKeys = Object.keys(myObject);
while (i < obKeys.length) {
  console.log(myObject[obKeys[i]]);
  i++;
}
///////////////////
const myArray= [3, 6, 9, 12, 27];
let i = 0;
while (i < myArray.length) {
  // get numbers in myArray between 10 and 20
  if (myArray[i] > 9 && myArray[i] < 21) {
    anotherArray.push(myArray[i]);
  }
  i++;
}

Ключевые слова break и continue могут использоваться в while цикле.

делать пока

Цикл do...while аналогичен циклу while, за исключением того, что код в блоке do повторяется по крайней мере один раз.

let num = 3;
let i = 0;
do {
  num += 1;
  i++;
}
while (i < 12);

Ключевые слова break и continue могут использоваться в do…while цикле.

Объединение петель

Вы можете комбинировать петли, чтобы работать вместе.

const keysToBeAdded = [
  "massOfSugar",
  "massOfBeans",
  "massOfCoffee"
];
const myObject = {
    massOfSugar: 5,
    randomMass: 50,
    massOfBeans: 19,
    massOfTomatoes: 50,
    massOfFish: 50,
    massOfCoffee: 5200,
};
let totalMass;
let massesToBeAdded = [];
const addValuesReducer = (accumulator, currentValue) => accumulator + currentValue;
for (const [key, value] of Object.entries(myObject)) {
  if (keysToBeAdded.includes(key)) {
    massesToBeAdded.push(value);
  }
}
totalMass = massesToBeAdded.reduce(addValuesReducer);
console.log(totalMass); // 5224

Размещение "for' петель"

Иногда бывает необходимо вложить одну for петлю в другую. Обычно это сравнение или обновление одного объекта относительно другого. Обычно вы используете вложенные циклы for с многомерными массивами. Многомерный массив - это просто массив массивов, например [[1, 6], [2,2,1], [5,6]].

function aggregate(multiDimArray) {
  let sum = 0;
  for (let indexA = 0; indexA < multiDimArray.length; indexA++) {
    const insideArray = multiDimArray[indexA];
    for (let indexB = 0; indexB < indexB.length; indexB++) {
      sum += insideArray[indexB];
    }
  }
  console.log(sum); // 23
}
const multiDimensionalArray = [[1, 6], [2,2,1], [5,6]];
aggregate(multiDimensionalArray);

Вы также можете использовать вложенные циклы с объектами, сначала преобразовав ключи / значения / ключи + значения вашего объекта в массивы с помощью методов Object.keys(), Object.values() или Object.entries() соответственно.

for...of vs for...in ?

Массивы - это просто объекты со скрытыми ключами, которые являются целыми числами. Это означает, что массив - это просто настраиваемый объект. И цикл for…of, и цикл for…in выполняют итерацию по этим объектам, но то, что они повторяют, отличается.

Цикл for…in случайным образом перебирает перечислимые свойства объекта. Он вернет все имена свойств, включая нецелочисленные имена и унаследованные свойства.

Цикл for…of последовательно выполняет итерацию по итерируемым значениям объекта, а повторяемый объект определяет, что повторяется.

Обратите внимание, что for…in возвращает скрытые индексные ключи массива, а for…of возвращает значения.

const myArray= [3, 6, 9, 12, 27];
for (const i in myArray) {
  console.log(i); // "0", "1", "2", "3", "4"
}
for (const i of myArray) {
  console.log(i); // "3", "6", "9", "12", "27"
}

Если вы измените прототип Object с помощью Object.prototype или Array.prototype, for…in вернет эти свойства, если вы не укажете, что этого не должно быть с hasOwnProperty. Ниже newArraykey возвращается даже с hasOwnProperty, потому что это свойство, которое вы добавили в myArray.

const myArray= [12, 6, 9];
// Array objects will also inherit this property "myObProp"
Object.prototype.myObProp =  function() {};
// Only array objects will inherit this property "myArrayProp" Array.prototype.myArrayProp =  function() {};
myArray.newArraykey = "surprise!";
for (const i in myArray) {
  console.log(i); // "0", "1", "2", "myObProp", "myArrayProp", "newArraykey"
}
for (const i in myArray) {
  if (myArray.hasOwnProperty(i)) {
    console.log(i); // "0", "1", "2", "newArraykey"
  }
}
for (const i of myArray) {
  console.log(i); // "12", "6", "9"
}

Заключительные выводы

Если вы хотите сделать один вывод из этой статьи, то вот что:

Цикл for…of является наиболее универсальным для использования. Чтобы перебирать объекты, сначала преобразуйте их в массивы с помощью одного из методов Object.keys(), Object.values() или Object.entries(), а затем используйте новые массивы в for…of - таким образом вы избегаете ошибочного перебора унаследованных свойств. Для перебора массивов просто используйте массивы в том виде, в котором они находятся в цикле for…of. Вы также можете использовать for…of для строк, если вам не нужен индекс.

Спасибо за чтение. Если вам понравилась эта статья, подумайте о подписке здесь на членство Medium, чтобы получить доступ к тысячам других статей.

В будущем я буду публиковать больше статей о JavaScript. Вам также может понравиться одна из других моих статей:





Образовательные ресурсы:

Цикл for
цикл for… in
Array.prototype. ForEach
цикл while
do… цикл while
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.includes
Array.prototype.find
Array.prototype.findIndex
Array.prototype.some
Array.prototype.every
Перечисляемость и владение свойствами