forEach中使用break和return的效果
// 使用break
let arr = ['1', '2', '3', '4', '5'];
arr.forEach(item=>{
if(item == '3') {
break
}
console.log(item) // 结果:报错 Uncaught SyntaxError: Illegal break statement at Array.forEach (<anonymous>)
})
// 使用 return
let arr = ['1', '2', '3', '4', '5'];
arr.forEach(item=>{
if(item == '3') {
return
}
console.log(item) // 结果:1 2 4 5 只能终止本次循环,执行下一次循环,并不能跳出循环
})
forEach的底层实现
forEach方法无法被直接终止的原因是它是一个迭代器(iterator)方法,它会遍历数组的每个元素并依次执行回调函数,底层实现中,并没有提供在循环过程中终止的机制。
迭代器(iterator):
迭代器(Iterator)是一种对象,它提供一种用于按顺序访问集合(如数组、字符串、Set、Map 等)中每个元素的方法。迭代器主要有两个关键方法:next() 和 [Symbol.iterator]()。
next()方法:当调用迭代器的next()方法时,它会返回一个包含两个属性的对象,即{ value, done }。其中,value表示当前迭代位置的值。done是一个布尔值,表示迭代是否已经结束。如果迭代尚未结束,则done为false;如果迭代已经完成,则done为true。
[Symbol.iterator]()方法:该方法返回迭代器对象自身,以实现可迭代协议。这使得我们可以通过使用for...of循环或通过调用Array.from()、spread操作符等方式来迭代集合中的元素。
迭代器的优点在于它提供了一种通用的访问集合元素的方式,无论集合的内部结构是什么样的。迭代器可以根据集合的不同类型和结构,提供适当的算法来遍历集合中的每个元素,使得我们可以轻松地处理集合的元素,执行各种操作,例如过滤、映射、查找等。
forEach内部使用了一个循环来遍历数组,但它并不像常规的循环(如for循环)那样具有可以被控制的迭代条件或跳出机制。当你调用forEach时,它将会按顺序遍历整个数组,并通过依次调用指定的回调函数来处理每个元素。
由于forEach方法是同步执行的,它会等待每个回调函数的执行完成后再继续遍历下一个元素。这意味着,即使在回调函数中尝试使用return语句或break语句,也无法真正中断或跳出整个forEach循环。
模拟实现
Array.prototype.forEachX = function(callback) {
const iterator = this[Symbol.iterator]();
let current = iterator.next();
while (!current.done) {
callback(current.value);
current = iterator.next();
}
};
在这个简单的示例中,我们首先获取数组的迭代器 iterator,通过 this[Symbol.iterator]() 方法来获取。然后,我们使用 while 循环遍历迭代器。在每次循环中,我们调用回调函数 callback 来处理当前迭代的值 current.value。循环会一直进行,直到迭代器的 next 方法返回的结果中的 done 属性为 true,表示迭代已经结束。
我们可以做一个简单的demo来进行查看
Array.prototype.forEachx = function(callback) {
const iterator = this[Symbol.iterator]();
let current = iterator.next();
console.log(current);
while (!current.done) {
const result = callback(current.value);
console.log(result)
current = iterator.next();
}
};
const myArray = [1, 2, 3, 4, 5];
myArray.forEachx((item) => {
console.log(item);
return '我是return'
})

可以看到这里我们实现了一个简单的forEach虽然我们return了 但是其实只是return到了 callback(current.value) 这里 给了 result一个返回值 并不会真正的终止while循环。如果需要实际对while值进行终止我们可以加入一个判断 当检测到 result === 'xxx' 的时候 break 这个while循环 这样他就不会去执行 iterator.next(); 这个迭代器也就终止掉了,实现如下:

解决方案
首先,我们不可能自己写一个迭代器去替换原本的forEach,除非你是最强王者,所以我们一般使用如下的解决方案来解决循环中需要跳过的问题。
- 通过for循环来实现
let arr = ['1', '2', '3', '4', '5'];
for(let i=0; i<arr.length; i++) {
if(arr[i] == '3') break;
console.log(arr[i]) // 得到结果 1 2
}
- 通过try···catch抛出异常的方式实现
let arr = ['1', '2', '3', '4', '5'];
try {
arr.forEach((item, index) => {
if (item === '3') {
throw new Error('End');
}
console.log(item); // 1 2
});
} catch (e) {
if (e.message === 'End') {
// 处理异常
console.error(e);
} else {
throw e;
}
}
- 通过reduce迭代实现,需设置一个中断flag
let arr = ['1', '2', '3', '4', '5'];
arr.reduce(function (p, c) {
if (c == '3') {
this.break = true
}
if (this.break) {
return
}
console.log(c) // 1 2
}, '')
Array.prototype.reduce 是 JavaScript 中用于迭代数组的方法之一。它可以对数组中的每个元素进行累加、计算或转换,并返回一个最终结果。
Array.prototype.reduce 方法接受一个回调函数作为参数,并且可以传递一个初始值作为可选的第二个参数。回调函数接受四个参数:累加器(accumulator)、当前值(current value)、当前索引(current index)和原始数组(original array)。
下面是 Array.prototype.reduce 方法的基本语法:
arr.reduce(callback, initialValue);
arr:要进行迭代的数组。callback:回调函数,用于对每个元素进行操作。它可以接受四个参数:累加器、当前值、当前索引和原始数组。initialValue:可选参数,用作初始值。
- 数组的另外两个方法some与every(不建议使用)
let arr = ['1', '2', '3', '4', '5'];
arr.some(item=> {
if (item == '3') {
return true;
}
console.log(item); // 1 2 true
});
let arr = ['1', '2', '3', '4', '5'];
arr.every(item=> {
if (item == '3') {
return false;
} else {
console.log(item);
return true;
}
}); // 1 2 false
Array.prototype.some(callback) 方法会对数组中的每个元素执行提供的回调函数,直到找到一个使得回调函数返回 true 的元素。如果找到这样的元素,some 方法就会立即返回 true,并停止继续迭代。如果数组中没有符合条件的元素,some 方法最后会返回 false。
Array.prototype.every(callback) 方法会对数组中的每个元素执行提供的回调函数,只有当所有元素都使得回调函数返回 true 时,every 方法才会返回 true。如果有任何一个元素使得回调函数返回 false,every 方法就会立即返回 false,并停止继续迭代。