解决js中forEach无法退出循环问题

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]()

  1. next() 方法:当调用迭代器的 next() 方法时,它会返回一个包含两个属性的对象,即 { value, done }。其中,
    • value 表示当前迭代位置的值。
    • done 是一个布尔值,表示迭代是否已经结束。如果迭代尚未结束,则 done 为 false;如果迭代已经完成,则 done 为 true
  2. [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。如果有任何一个元素使得回调函数返回 falseevery 方法就会立即返回 false,并停止继续迭代。

文章链接:http://iwebg.cn/index.php/2023/08/09/%e8%a7%a3%e5%86%b3js%e4%b8%adforeach%e6%97%a0%e6%b3%95%e9%80%80%e5%87%ba%e5%be%aa%e7%8e%af%e9%97%ae%e9%a2%98/

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇