предговор

Честно казано, не обичам да използвам forEach, защото някои от грешките, които причинява, винаги са толкова непреднамерени, прегледайте причините, които не ми харесват.

Причина 1: не поддържа обработка на асинхронни функции

Нека първо да разгледаме един пример:

async function test() {
     let arr = [3, 2, 1]
     arr.forEach(async item => {
         const res = await mockSync(item)
         console. log(res)
     })
     console. log('end')
}

function mockSync(x) {
     return new Promise((resolve, reject) => {
         setTimeout(() => {
                 resolve(x)
         }, 1000 * x)
     })
}
test()
The result we expect is:
3
2 
1
end

but actually outputs:
end
1
2
3

JavaScriptМетодът в forEach()е синхронен метод, който не поддържа работа с асинхронни функции. Ако forEachизпълните асинхронна функция в , forEach()тя не може да изчака асинхронната функция да завърши, тя ще продължи да изпълнява следващия елемент. forEach()Това означава, че няма гаранция за реда, в който ще се изпълняват асинхронни задачи, ако в тях се използва асинхронна функция.

алтернативен forEachway

1. Метод 1

Можете да използвате напр. map(), filter(), reduce()и т.н. които поддържат връщане във функциите Promiseи ще чакат всички обещания да бъдат изпълнени.

Примерният код за използване на map()и Promise.all()за обработка на асинхронни функции е както следва:

const arr = [1, 2, 3, 4, 5];
async function asyncFunction(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}
const promises = arr.map(async (num) => {
  const result = await asyncFunction(num);
  return result;
});
Promise.all(promises).then((results) => {
  console.log(results); // [2, 4, 6, 8, 10]
});

Тъй като използвахме awaitключовата дума в async функцията, map()методът изчаква async функцията да завърши и връща резултата, така че да можем да обработваме async функцията правилно.

2. Метод 2

използва for цикъл за обработка на асинхронни функции

const arr = [1, 2, 3, 4, 5];

async function asyncFunction(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}

async function processArray() {
  const results = [];
  for (let i = 0; i < arr.length; i++) {
    const result = await asyncFunction(arr[i]);
    results.push(result);
  }
  console.log(results); // [2, 4, 6, 8, 10]
}

processArray();

Причина 2: Не могат да се уловят грешки в асинхронните функции

Ако асинхронна функция хвърля грешка по време на изпълнение, forEach()няма начин да се хване тази грешка. Това означава, че дори ако възникне грешка в асинхронна функция, forEach()execution ще продължи.

Причина 3: Няма начин да прекъснете или излезете от цикъла, освен да хвърлите изключение

forEach()Method не поддържа използването на израза breakили continue за излизане от цикъл или за пропускане на елемент. Ако трябва да излезете от цикъл или да пропуснете елемент, трябва да използвате forцикъл или друг метод за поддържане breakили continueизявление.

Причина 4: forEach изтрива собствените си елементи, индексът не може да бъде нулиран

Не можем да контролираме стойността на индекса в forEach, тя ще се увеличава автоматично само докато стане по-голяма от дължината на масива и ще излезе от цикъла. Следователно е невъзможно да се изтрие, за да нулира индекса. Нека да разгледаме един прост пример:

let arr = [1,2,3,4]
arr.forEach((item, index) => {
    console.log(item); // 1 2 3 4
    index++;
});

Причина 5: това сочи към проблема

В рамките на forEach()a метод, this ключовата дума се отнася до обекта, на който се извиква методът. thisВъпреки това, обхватът на ключова дума може да бъде проблематичен, когато се използват нормални функции или функции със стрелки като аргументи. Във функция със стрелка ключовата дума this се отнася до обекта, в който е дефинирана функцията. В нормална функция ключовата дума this се отнася до обекта, който е извикал функцията. Ако трябва да се уверите, че обхватът на ключовата дума this е правилен, можете да използвате метода bind(), за да обвържете обхвата на функцията. Ето пример за проблем с обхвата на ключова дума forEach()в метод this:

const obj = {
  name: "Alice",
  friends: ["Bob", "Charlie", "Dave"],
  printFriends: function () {
    this.friends.forEach(function (friend) {
      console.log(this.name + " is friends with " + friend);
    });
  },
};
obj.printFriends();

В този пример ние дефинираме objобект с име, който има printFriends()method. В printFriends()метода, ние използваме forEach()метода за итерация върху friendsмасива и използваме нормалната функция, за да отпечатаме името на всеки приятел и свойствата objна обекта name. Въпреки това, когато стартираме този код, изходът е:

undefined is friends with Bob
undefined is friends with Charlie
undefined is friends with Dave

Това е така, защото forEach()когато нормална функция се използва в метод, обхватът на функцията не е printFriends()обектът, извикващ метода, а глобалният обхват. Следователно свойствата на обекта не могат да бъдат достъпни в рамките на тази функция obj.

За да заобиколите това, можете да използвате bind()methods за обвързване на обхвата на функцията или да използвате функции със стрелки, за да дефинирате функции за обратно извикване. Ето bind()пример на код, който използва метод за решаване на проблема:

const obj = {
   name: "Alice",
   friends: ["Bob", "Charlie", "Dave"],
   printFriends: function () {
     this.friends.forEach(
       function (friend) {
         console.log(this.name + " is friends with " + friend);
       }.bind(this) // Use the bind() method to bind the scope of the function
     );
   },
};
obj. printFriends();

В този пример използваме bind()methods, за да обвържем обхвата на функцията, който обвързва обхвата на функцията с objобекта. След изпълнение на кода изходът е:

Alice is friends with Bob 
Alice is friends with Charlie 
Alice is friends with Dave

Като използваме bind()methods за обвързване на обхвата на функцията, можем да осъществим правилен достъп до objсвойствата на обекта.

Друго решение е да използвате функции със стрелки. Тъй като функцията стрелка няма свой собствен this, тя наследява от обхвата, който е в this. И така, във функция със стрелка this ключовата дума се отнася до обекта, в който е дефинирана функцията. кодово съкращение.

Причина 6: Производителността на forEach е по-ниска от тази на цикъла for

for: Цикълът for няма допълнителен стек за извикване на функция и контекст, така че внедряването му е най-просто.
forEach: Сигнатурата на ForEach съдържа параметри и контекст, така че производителността ще бъде по-ниска от for цикъла.

Причина 7: Изтрити или неинициализирани елементи ще бъдат пропуснати

// skip uninitialized values
const array = [1, 2, /* empty */, 4];
let num = 0;

array. forEach((ele) => {
   console. log(ele);
   num++;
});

console.log("num:",num);

//  1
//  2 
// 4
// num: 3

// skip deleted values
const words = ['one', 'two', 'three', 'four'];
words. forEach((word) => {
   console. log(word);
   if (word === 'two') {
   // When the item containing the value `two` is reached, the first item of the entire array is removed
   // This causes all remaining items to be moved up one position. Since the element `four` is at the beginning of the array, `three` is skipped.
     words.shift(); //'one' will be removed from the array
   }
}); // one // two // four

console.log(words); // ['two', 'three', 'four']

Причина 8: използването на forEach няма да промени оригиналния масив

forEach() Когато бъде извикан, оригиналният масив, т.е. масивът, който го е извикал, няма да бъде променен. Но този обект може да бъде променен от предадената функция за обратно извикване.

// example one
const array = [1, 2, 3, 4];
array.forEach(ele => { ele = ele * 3 })
console.log(array); // [1,2,3,4]

// Solution, change the original array
const numArr = [33,4,55];
numArr.forEach((ele, index, arr) => {
     if (ele === 33) {
         arr[index] = 999
     }
})
console.log(numArr); // [999, 4, 55]

// Example 2
const changeItemArr = [{
     name: 'wxw',
     age: 22
}, {
     name: 'wxw2',
     age: 33
}]
changeItemArr.forEach(ele => {
     if (ele.name === 'wxw2') {
         ele = {
             name: 'change',
             age: 77
         }
     }
})
console.log(changeItemArr); // [{name: "wxw", age: 22},{name: "wxw2", age: 33}]

// solution
const allChangeArr = [{ name: 'wxw', age: 22}, { name: 'wxw2', age: 33}]
allChangeArr.forEach((ele, index, arr) => {
     if (ele.name === 'wxw2') {
         arr[index] = {
             name: 'change',
             age: 77
         }
     }
})
console.log(allChangeArr); // // [{name: "wxw", age: 22},{name: "change", age: 77}]
copy code

Е, ще го използвам for...ofвместоforEach