ChatGPT解决这个技术问题 Extra ChatGPT

短路 Array.forEach 就像调用 break

[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

如何使用 JavaScript 中的新 forEach 方法执行此操作?我试过 return;return false;breakbreak 崩溃,return 只继续迭代。

值得注意的是,虽然 return 确实继续迭代,但它会跳过块中它之后的任何代码。以这段代码为例: [1,2,3].forEach(function(el) { if(el === 2) { console.log(`Match on 2!`); return; } console.log(el); }); 。匹配 2 时将跳过 console.log(el);

R
Ruslan López

forEach 中没有内置的 break 功能。要中断执行,您必须抛出某种异常。例如。

var BreakException = {};尝试 { [1, 2, 3].forEach(function(el) { console.log(el); if (el === 2) throw BreakException; }); } catch (e) { if (e !== BreakException) throw e; }

JavaScript 异常并不是非常漂亮。如果您确实需要在其中使用 break,则传统的 for 循环可能更合适。

使用数组#some

相反,请使用 Array#some

[1, 2, 3].some(function(el) { console.log(el); return el === 2; });

这是因为 some 在任何以数组顺序执行的回调返回 true 时立即返回 true,从而使其余部分的执行短路。

some、它的逆 every(将在 return false 处停止)和 forEach 都是 ECMAScript 第五版方法,需要将它们添加到缺少它们的浏览器的 Array.prototype 中。


我认为“一些”在这里很好,为什么不使用早期退出优化-
感谢您关注 someevery,这应该在答案中排在首位。无法理解为什么人们认为它的可读性较低。真是太棒了!
Array#some 的使用非常好。首先它与大多数浏览器兼容,包括 ie9 和 firefox 1.5 也非常好用。我的示例用例是在范围[a,b] 数组中查找索引,其中一个数字位于下边界和上边界对之间,测试并在找到时返回 true。 for..of 将是下一个最佳解决方案,但仅适用于较新的浏览器。
异常处理绝不能用作控制流。时期。
@frank 我 wrote an esolang with only one loop construct: forever。所有其他循环构造都由 forever 和适当的可迭代异常组成,例如 ContinueIterationStopIteration(其中 break 是要引发的宏)。一方面:从不。另一方面:总是。在紧握的手上:有时?您评论中的“PERIOD”暗示可能有引用来支持您的建议?
h
hitautodestruct

现在,在 ECMAScript2015(又名 ES6)中使用新的 for of loop 有更好的方法来执行此操作。例如,此代码不打印数字 5 之后的数组元素:

让 arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for (let el of arr) { console.log(el); if (el === 5) { 休息; } }

从文档:

for...in 和 for...of 语句都会迭代某些内容。它们之间的主要区别在于它们迭代的内容。 for...in 语句以原始插入顺序迭代对象的可枚举属性。 for...of 语句迭代可迭代对象定义要迭代的数据。

迭代中需要索引吗?您可以使用 Array.entries()

for (const [index, el] of arr.entries()) {
  if ( index === 5 ) break;
}

@superhero 您可以在 for...of 循环中获取元素的索引,您只需使用 entries。 for (const [index, element] of someArray.entries()) { // ... }
不建议不要在数组中使用 for...in 吗?
@emostafa 您对不推荐用于数组的 for in 循环是正确的,但这种方法实际上使用了 for of 循环。
这是“for of”,这是一个非常干净的解决方案……但这也是 ES6 的一个特性,所以请注意,这只有在您的环境设置为 ES6 时才有效。
我发现自己经常使用这个解决方案,我也将它用于对象。对于对象,您可以执行 Object.entries(myObject),然后像使用数组的 for..in 一样使用它。请注意,JS 数组基本上是底层对象:blog.niftysnippets.org/2011/01/myth-of-arrays.html
V
Valdemar_Rudolfovich

您可以使用 every 方法:

[1,2,3].every(function(el) {
    return !(el === 1);
});

ES6

[1,2,3].every( el => el !== 1 )

对于旧浏览器支持使用:

if (!Array.prototype.every)
{
  Array.prototype.every = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          !fun.call(thisp, this[i], i, this))
        return false;
    }

    return true;
  };
}

更多详情here


现在在 ES6 中既漂亮又干净 - [1,2,3].every( el => el !== 1 )
@Valdemar,但是every 保证调用是按顺序进行的吗?
@Pacerier,您可以在ES6规范中看到索引k从0开始并递增1的算法:http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.every
@Pacerier,是的,大多数流行的实现都可以正常工作。如果您关心嵌入式实现,通常是 Opera 或 webkit。方法 every 为数组中存在的每个元素调用一次 callbackfn,按升序排列,直到找到一个 callbackfn 返回 false 的元素。还要看第 7 步。让 k 为 0。和 8.e 将 k 增加 1。
更改代码的意图。很坏。
R
Rahul Desai

引用 MDN documentation of Array.prototype.forEach()

除了抛出异常之外,没有其他方法可以停止或中断 forEach() 循环。如果您需要这种行为,则 .forEach() 方法是错误的工具,请改用普通循环。如果您正在测试谓词的数组元素并且需要布尔返回值,则可以使用 every() 或 some() 代替。

根据@bobince 的建议,对于您的代码(在问题中),请改用 Array.prototype.some() 。它非常适合您的用例。

Array.prototype.some() 对数组中存在的每个元素执行一次回调函数,直到找到一个回调函数返回一个真值(当转换为布尔值时变为真值)。如果找到这样的元素, some() 立即返回 true。否则, some() 返回 false。回调仅针对已分配值的数组索引调用;对于已被删除或从未被赋值的索引,它不会被调用。


这是正确的答案。 'some' 完全符合 foreach/break 的作用。它循环直到迭代 n = true。
Sonar 标记它,而不是使用 array.some() 的返回值。逻辑是仅将其用于循环。
W
Weston Ganger

不幸的是,在这种情况下,如果您不使用 forEach 会好很多。而是使用常规的 for 循环,它现在将完全按照您的预期工作。

var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
  if (array[i] === 1){
    break;
  }
}

令我震惊的是,与这个正确答案的更高性能、更少代码和更好的可读性相比,最高投票是可能的最差实现。抛出异常......真的吗?传统的 for 循环是否不够 kewl?
@gdbj 我同意你的说法并使用了这种方法,但真正让我震惊的是,如果没有这些黑客,就无法退出 forEach,现在这是糟糕的设计。
@gdbj 我也同意,但问题更多在于 Stack Overflow 及其指南。该问题专门要求解决 Array.forEach 循环。公认的答案是……以一种令人难以置信的丑陋方式。从技术上讲,这个答案是不正确的,即使它以一种经过验证的方式实现了相同的目标。
M
Mahdi

从您的代码示例中,看起来 Array.prototype.find 就是您要查找的内容:Array.prototype.find()Array.prototype.findIndex()

[1, 2, 3].find(function(el) {
    return el === 2;
}); // returns 2

v
vittore

考虑使用 jqueryeach 方法,因为它允许在回调函数中返回 false:

$.each(function(e, i) { 
   if (i % 2) return false;
   console.log(e)
})

Lodash 库还提供了可以与 map/reduce/fold 等链接的 takeWhile 方法:

var users = [
  { 'user': 'barney',  'active': false },
  { 'user': 'fred',    'active': false },
  { 'user': 'pebbles', 'active': true }
];

_.takeWhile(users, function(o) { return !o.active; });
// => objects for ['barney', 'fred']

// The `_.matches` iteratee shorthand.
_.takeWhile(users, { 'user': 'barney', 'active': false });
// => objects for ['barney']

// The `_.matchesProperty` iteratee shorthand.
_.takeWhile(users, ['active', false]);
// => objects for ['barney', 'fred']

// The `_.property` iteratee shorthand.
_.takeWhile(users, 'active');
// => []

使用 jQuery 的好理由。本机javascript中的forEach仍然缺乏。
@AlexGrande jQuery 的 forEach 和 JavaScript 的 forEach 不兼容。
JavaScript 在很多地方都使用,但 jQuery 不是一个选项。
C
Chris West

如果您想使用 Dean Edward's suggestion 并抛出 StopIteration 错误以跳出循环而不必捕获错误,则可以使用以下函数 (originally from here):

// Use a closure to prevent the global namespace from be polluted.
(function() {
  // Define StopIteration as part of the global scope if it
  // isn't already defined.
  if(typeof StopIteration == "undefined") {
    StopIteration = new Error("StopIteration");
  }

  // The original version of Array.prototype.forEach.
  var oldForEach = Array.prototype.forEach;

  // If forEach actually exists, define forEach so you can
  // break out of it by throwing StopIteration.  Allow
  // other errors will be thrown as normal.
  if(oldForEach) {
    Array.prototype.forEach = function() {
      try {
        oldForEach.apply(this, [].slice.call(arguments, 0));
      }
      catch(e) {
        if(e !== StopIteration) {
          throw e;
        }
      }
    };
  }
})();

上面的代码将使您能够运行以下代码,而无需执行自己的 try-catch 子句:

// Show the contents until you get to "2".
[0,1,2,3,4].forEach(function(val) {
  if(val == 2)
    throw StopIteration;
  alert(val);
});

要记住的一件重要事情是,如果 Array.prototype.forEach 函数已经存在,它只会更新它。如果它不存在,它不会修改它。


M
Max

简短的回答:为此使用 for...break 或更改您的代码以避免破坏 forEach。不要使用 .some().every() 来模拟 for...break。重写代码以避免 for...break 循环,或使用 for...break。每次您将这些方法用作for...break替代方法时,上帝都会杀死小猫。

长答案:

.some().every() 都返回 boolean 值,如果传递函数返回 true 的任何元素,则 .some() 返回 true,如果传递函数返回 {7 的任何元素,则每个返回 false }。这就是函数的含义。使用没有意义的函数比使用表格而不是 CSS 更糟糕,因为它让阅读您的代码的每个人都感到沮丧。

此外,将这些方法用作 for...break 替代方法的唯一可能方法是产生副作用(在 .some() 回调函数之外更改一些变量),这与 for...break 没有太大区别。

因此,使用 .some().every() 作为 for...break 循环替代方案并非没有副作用,这并不比 for...break 更干净,这令人沮丧,所以这并不是更好。

您可以随时重写您的代码,以便在 for...break 中不需要。您可以使用 .filter() 过滤数组,您可以使用 .slice() 拆分数组等等,然后使用 .forEach().map() 作为数组的那一部分。


使用 .filter 实际上是许多破坏用例的合适解决方案。
性能呢?如果经常使用,过滤器不会影响性能吗?
是的,过滤器阵列原型可能会变得很重。我喜欢它,但如果过度使用它可能会影响性能。
如果您需要性能,@tfrascaroli 使用 for...break 循环。 for 循环是比 .forEach().any().map().filter() 等性能最高的迭代工具。
如果您打算构建表格,为什么使用表格进行布局是一件坏事?实际上,在需要表格时不使用表格就像使用一些,而不是破坏。表格与可读性无关。
A
Alex

如前所述,您不能破坏 .forEach()

这是使用 ES6 迭代器进行 foreach 的一种更现代的方式。允许您在迭代时直接访问 index/value

const array = ['one', 'two', 'three'];

for (const [index, val] of array.entries()) {
  console.log('item:', { index, val });
  if (index === 1) {
    console.log('break!');
    break;
  }
}

输出:

item: { index: 0, val: 'one' }
item: { index: 1, val: 'two' }
break!

链接

Array.prototype.entries()

迭代器和生成器

解构赋值


c
c24w

我提出的另一个概念:

函数 forEach(array, cb) { var shouldBreak;函数 _break() { shouldBreak = true; } for (var i = 0, bound = array.length; i < bound; ++i) { if (shouldBreak) { break; } cb(数组[i],我,数组,_break); } } // 用法 forEach(['a','b','c','d','e','f'], function (char, i, array, _break) { console.log(i, char); if (i === 2) { _break(); } });


语法类似于 [NSArray enumerateObjectsUsingBlock],谢谢!
@Drenai 签名类似于原生 Array.prototype.forEach()forbreak 早在问这个问题之前就已经存在了; OP 正在使用功能更强大的 forEach 来寻找该行为。
@Drenai 现在删除了他们的评论(但留下了反对票),其中提到当您可以使用 for...inbreak 解决问题时,此解决方案的签名很难记住且不必要。
t
tennisgent

这只是我想出的解决问题的方法......我很确定它解决了原始提问者遇到的问题:

Array.prototype.each = function(callback){
    if(!callback) return false;
    for(var i=0; i<this.length; i++){
        if(callback(this[i], i) == false) break;
    }
};

然后您可以使用以下方法调用它:

var myarray = [1,2,3];
myarray.each(function(item, index){
    // do something with the item
    // if(item != somecondition) return false; 
});

在回调函数中返回 false 将导致中断。如果这实际上不起作用,请告诉我。


=== false 可能比 == false 更好,因此您不必显式返回 true(或真值)来继续循环,以免某些控制路径不返回值并且循环意外中断。
3
3rdEden

如果您不需要在迭代后访问您的数组,您可以通过将数组的长度设置为 0 来解决问题。如果您在迭代后仍然需要它,您可以使用 slice 克隆它。

[1,3,4,5,6,7,8,244,3,5,2].forEach(function (item, index, arr) {
  if (index === 3) arr.length = 0;
});

或使用克隆:

var x = [1,3,4,5,6,7,8,244,3,5,2];

x.slice().forEach(function (item, index, arr) {
  if (index === 3) arr.length = 0;
});

这是一个比在代码中抛出随机错误更好的解决方案。


干得好 :) 但是如果在将 array.length 分配给 0 之后有一些操作,它们将适用于当前迭代,所以有时在分配后使用 return 可能会更好
R
RussellUresti

在另一个网站上找到了这个解决方案。您可以将 forEach 包装在 try / catch 场景中。

if(typeof StopIteration == "undefined") {
 StopIteration = new Error("StopIteration");
}

try {
  [1,2,3].forEach(function(el){
    alert(el);
    if(el === 1) throw StopIteration;
  });
} catch(error) { if(error != StopIteration) throw error; }

更多详细信息:http://dean.edwards.name/weblog/2006/07/enum/


不要将异常用作控制流语句。使用它来处理意外结果。
j
jamos

这是一个 for 循环,但像 forEach() 一样在循环中维护对象引用,但您可以中断。

var arr = [1,2,3];
for (var i = 0, el; el = arr[i]; i++) {
    if(el === 1) break;
}

G
Gigol

尝试“查找”:

var myCategories = [
 {category: "start", name: "Start", color: "#AC193D"},
 {category: "action", name: "Action", color: "#8C0095"},
 {category: "exit", name: "Exit", color: "#008A00"}
];

function findCategory(category) {
  return myCategories.find(function(element) {
    return element.category === category;
  });
}

console.log(findCategory("start"));
// output: { category: "start", name: "Start", color: "#AC193D" }

O
Olcay Ertaş

还有一种方法:

var wageType = types.filter(function(element){
  if(e.params.data.text == element.name){
    return element;
  }
});
console.dir(wageType);

这是过滤方法的正确用法吗?我猜回调返回一个布尔值,不管它,回调被称为数组元素,直到最后一个。
p
public override

我为此目的使用 nullhack,它尝试访问 null 的属性,这是一个错误:

try {
  [1,2,3,4,5]
  .forEach(
    function ( val, idx, arr ) {
      if ( val == 3 ) null.NULLBREAK;
    }
  );
} catch (e) {
  // e <=> TypeError: null has no properties
}
//

为什么不只是 throw BREAK
您可以简单地使用 FOOBARED,它会引发错误。
Y
Yiling

使用 array.prototype.every 函数,它为您提供中断循环的实用程序。在此处查看示例 Javascript documentation on Mozilla developer network


D
Dmitri Sologoubenko

同意@bobince,赞成。

另外,仅供参考:

Prototype.js 有一些为此目的:

<script type="text/javascript">
  $$('a').each(function(el, idx) {
    if ( /* break condition */ ) throw $break;
    // do something
  });
</script>

$break 将由 Prototype.js 在内部捕获和处理,打破“每个”循环,但不会产生外部错误。

有关详细信息,请参阅 Prototype.JS API

jQuery 也有办法,只要在处理程序中返回 false 就可以提前中断循环:

<script type="text/javascript">
  jQuery('a').each( function(idx) {
    if ( /* break condition */ ) return false;
    // do something

  });
</script>

有关详细信息,请参阅 jQuery API


J
Justus Romijn

如果您想保留 forEach 语法,这是一种保持其效率的方法(尽管不如常规 for 循环好)。立即检查知道是否要跳出循环的变量。

此示例使用匿名函数围绕您需要存储 done 信息的 forEach 创建一个函数范围

(function(){ var element = document.getElementById('printed-result'); var done = false; [1,2,3,4].forEach(function(item){ if(done){ return; } var text = document.createTextNode(item); element.appendChild(text); if (item === 2){ done = true; return; } }); })();

我的两分钱。


U
Ula

如果您需要根据您的情况下已经在数组中的元素的值进行中断(即,如果中断条件不依赖于在数组被分配其元素值后可能发生变化的运行时变量),您也可以使用组合slice()indexOf() 如下所示。

如果您需要在 forEach 到达“Apple”时中断,您可以使用

var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var fruitsToLoop = fruits.slice(0, fruits.indexOf("Apple"));
// fruitsToLoop = Banana,Orange,Lemon

fruitsToLoop.forEach(function(el) {
    // no need to break
});

in W3Schools.com 所述, slice() 方法将数组中的选定元素作为新数组对象返回。原始数组不会改变。

JSFiddle 中查看

希望它可以帮助某人。


W
Willie

为什么不尝试将函数包装在 Promise 中?

我提出它的唯一原因是我在 API 中使用了一个与 forEach 类似的函数。我不希望它在找到值后继续迭代,我需要返回一些东西,所以我只是要解决一个 Promise 并这样做。

traverseTree(doc): Promise<any> {
  return new Promise<any>((resolve, reject) => {
    this.gridOptions.api.forEachNode((node, index) => {
    //the above function is the one I want to short circuit.
      if(node.data.id === doc.id) {
        return resolve(node);
      }
    });
  });
}

然后你需要做的就是对结果做一些事情,比如

this.traverseTree(doc).then((result) => {
   this.doSomething(result);
});

我上面的例子是打字稿,只需忽略类型。逻辑应该有希望帮助你“打破”你的循环。


m
martyman

这不是最有效的,因为您仍然循环所有元素,但我认为可能值得考虑非常简单:

let keepGoing = true;
things.forEach( (thing) => {
  if (noMore) keepGoing = false;
  if (keepGoing) {
     // do things with thing
  }
});

continue 是关键字,您的代码是语法错误。
鉴于您仍然使用 ES6,您应该像往常一样切换到 for of 循环和 break;
固定的,真实的——但为了简洁起见,主要使用 es6
I
Ian Clark

您可以按照以下对我有用的代码:

 var     loopStop = false;
YOUR_ARRAY.forEach(function loop(){
    if(loopStop){ return; }
    if(condition){ loopStop = true; }
});

为什么是-1?它并不比捕获异常更丑陋,这是一个更大的黑客恕我直言。
D
Durgpal Singh

我知道这不是正确的方式。它不是打破循环。这是一个朱加德

让结果=真; [1, 2, 3].forEach(function(el) { if(result){ console.log(el); if (el === 2){ result = false; } } });


N
Ngatia Frankline

在 React 中突破内置的 Array.prototype.map 函数 esp

这里要注意的关键是使用语句 return 来 BREAK

let isBroken = false;

colours.map(item => {
    if (isBroken) {
        return;
    }
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        isBroken = true;
        return;
    }
});

更多信息在这里:https://www.codegrepper.com/code-examples/javascript/break+out+of+map+javascript


V
Venryx

您可以创建允许 breakcontinuereturn 甚至 async/awaitforEach 变体:(用 TypeScript 编写的示例)

export type LoopControlOp = "break" | "continue" | ["return", any];
export type LoopFunc<T> = (value: T, index: number, array: T[])=>LoopControlOp;

Array.prototype.ForEach = function ForEach<T>(this: T[], func: LoopFunc<T>) {
    for (let i = 0; i < this.length; i++) {
        const controlOp = func(this[i], i, this);
        if (controlOp == "break") break;
        if (controlOp == "continue") continue;
        if (controlOp instanceof Array) return controlOp[1];
    }
};

// this variant lets you use async/await in the loop-func, with the loop "awaiting" for each entry
Array.prototype.ForEachAsync = async function ForEachAsync<T>(this: T[], func: LoopFunc<T>) {
    for (let i = 0; i < this.length; i++) {
        const controlOp = await func(this[i], i, this);
        if (controlOp == "break") break;
        if (controlOp == "continue") continue;
        if (controlOp instanceof Array) return controlOp[1];
    }
};

用法:

function GetCoffee() {
    const cancelReason = peopleOnStreet.ForEach((person, index)=> {
        if (index == 0) return "continue";
        if (person.type == "friend") return "break";
        if (person.type == "boss") return ["return", "nevermind"];
    });
    if (cancelReason) console.log("Coffee canceled because: " + cancelReason);
}

这将要求任何人返回并阅读 Array 原型,这似乎并不简单而且浪费时间
不,“继续”、“中断”和“返回”字符串/运算符在 ForEach 函数中处理(它不是通用 forEach 函数)——调用者不需要读取 Array 原型上的任何内容。也就是说,我实际上并没有在生产代码中使用上述解决方案——它只是展示了如何在类似 forEach 的函数中完成这些不同的运算符,如果他们愿意的话。
是的。而且 - 如果我遇到这种情况 - 乍一看还不清楚。
R
Robby Lebotha

我使用 return false,它对我有用。


N
Neoheurist
const Book = {"Titles":[                          
    {"Book3" : "BULLETIN 3"},
    {"Book1" : "BULLETIN 1"},
    {"Book2" : "BULLETIN 2"}    
]}

const findbystr = function(str) { 
    Book.Titles.forEach(function(data) { 
        if (typeof data[str] != 'undefined') {
            return data[str];
        } 
    }, str) 
}

book = findbystr('Book1');

console.log(book);

我喜欢这种方法(我已经编辑了代码,以便在找到值时立即从函数返回) - 如果你要写下 forEach 中的返回如何具有 break 语句的效果,你可能会产生一些对该选项投赞成票。