arr.forEach(callback(currentValue [, index [, array]])[, thisArg])别被这复杂的语法吓到,我们来逐个拆解。
async function test() { let arr = [3, 2, 1]; arr.forEach(async item => { const res = await mockAsync(item); console.log(res); }); console.log('end'); } function mockAsync(x) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(x); }, 1000 * x); }); } test();预期结果:
async function test() { let arr = [3, 2, 1]; for (let item of arr) { const res = await mockAsync(item); console.log(res); } console.log('end'); } // 堆代码 duidaima.com function mockAsync(x) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(x); }, 1000 * x); }); } test();输出结果:
let arr = [1, 2, 3]; try { arr.forEach(item => { if (item === 2) { throw('error'); } console.log(item); }); } catch(e) { console.log('e:', e); } // 输出结果: // 1 // e: error在这个例子中,我们尝试通过抛出异常来中断forEach循环。虽然这种方法在某些情况下有效,但并不是优雅或推荐的做法。
let arr = [1, 2, 3]; for (let item of arr) { if (item === 2) { break; // 中断循环 } console.log(item); } // 输出结果: // 1在这个例子中,当遇到元素2时,循环会被中断,从而避免输出2和3。
let arr = [1, 2, 3, 4]; arr.forEach((item, index) => { console.log(item); // 输出: 1 2 3 4 index++; });在这个例子中,forEach遍历数组 arr,输出每个元素的值。虽然我们尝试在循环内部递增 index,但这并不会影响forEach的内部机制。forEach中的索引是自动管理的,并且在每次迭代时都会自动递增。
let arr = [1, 2, 3, 4]; arr.forEach((item, index) => { if (item === 2) { arr.splice(index, 1); // 尝试删除元素2 } console.log(item); // 输出: 1 2 4 }); console.log(arr); // 输出: [1, 3, 4]在这个例子中,当我们删除元素2时,forEach并不会重置或调整索引,因此它继续处理原数组中的下一个元素。这导致元素3被跳过,因为原来的元素3现在变成了元素2的位置。
let arr = [1, 2, 3, 4]; for (let i = 0; i < arr.length; i++) { if (arr[i] === 2) { arr.splice(i, 1); // 删除元素2 i--; // 调整索引 } else { console.log(arr[i]); // 输出: 1 3 4 } } console.log(arr); // 输出: [1, 3, 4]5、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方法。我们使用forEach方法遍历friends数组,并使用常规函数来打印每个朋友的名字和obj对象的name属性。然而,运行这段代码时,输出如下:
undefined is friends with Bob undefined is friends with Charlie undefined is friends with Dave这是因为在forEach方法中使用常规函数时,该函数的作用域不是调用printFriends方法的对象,而是全局作用域。因此,无法访问obj对象的属性。
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) // 使用bind方法绑定函数的作用域 ); }, }; obj.printFriends();运行这段代码,输出如下:
Alice is friends with Bob Alice is friends with Charlie Alice is friends with Dave通过使用bind方法绑定函数的作用域,我们可以正确地访问obj对象的属性。
const obj = { name: "Alice", friends: ["Bob", "Charlie", "Dave"], printFriends: function () { this.friends.forEach((friend) => { console.log(this.name + " is friends with " + friend); }); }, }; obj.printFriends();运行这段代码,输出如下:
Alice is friends with Bob Alice is friends with Charlie Alice is friends with Dave使用箭头函数,我们可以确保this关键字指向正确的对象,从而正确访问对象的属性。
上下文处理:forEach 方法需要处理函数的上下文和参数,这些操作都会消耗额外的时间和资源。
const array = [1, 2, /* 空 */, 4]; let num = 0; array.forEach((ele) => { console.log(ele); num++; }); console.log("num:", num); // 输出结果: // 1 // 2 // 4 // num: 3在这个例子中,数组中的第三个元素未初始化,forEach直接跳过了它。因此,虽然数组的长度是4,但实际被遍历的元素只有3个。
const words = ['one', 'two', 'three', 'four']; words.forEach((word) => { console.log(word); if (word === 'two') { words.shift(); // 删除数组中的第一个元素 'one' } }); // 输出结果: // one // two // four console.log(words); // ['two', 'three', 'four']在这个例子中,当遍历到元素 'two' 时,执行了 words.shift(),删除了数组中的第一个元素 'one'。由于数组元素向前移动,元素 'three' 被跳过,forEach 直接处理新的第三个元素 'four'。
const array = [1, 2, 3, 4]; array.forEach(ele => { ele = ele * 3 }) console.log(array); // [1, 2, 3, 4]在这个例子中,forEach方法并没有改变原数组。虽然在回调函数中对每个元素进行了乘3的操作,但这些操作并没有反映在原数组中。如果希望通过forEach改变原数组,需要直接修改数组元素的值,而不是简单地对元素进行赋值。
const numArr = [33, 4, 55]; numArr.forEach((ele, index, arr) => { if (ele === 33) { arr[index] = 999; } }); console.log(numArr); // [999, 4, 55]在这个例子中,我们通过forEach方法直接修改了数组中的元素,从而改变了原数组。
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}]在这个例子中,尝试对数组中的对象进行替换操作,但这种方式并不会改变原数组中的对象。
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}]在这个例子中,通过索引直接修改数组中的对象,从而实现了对原数组的修改。