短路Array.forEach就像调用break

时间:2010-04-14 21:57:32

标签: javascript arrays

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

如何在JavaScript中使用新的forEach方法执行此操作?我试过了return;return false;breakbreak崩溃,return除了继续迭代之外什么都不做。

36 个答案:

答案 0 :(得分:1746)

breakforEach没有内置功能。要中断执行,你必须抛出某种异常。例如

var BreakException = {};

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

JavaScript异常并不是非常漂亮。如果你真的需要for里面的传统break循环可能更合适。

使用Array#some

相反,请使用Array#some

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

这是有效的,因为只要以数组顺序执行的任何回调,some返回true,就会返回true,从而使其余的执行短路。

some,其倒数every(将停留在return false上)和forEach都是ECMAScript第五版方法,需要添加到{ {1}}在他们失踪的浏览器上。{/ p>

答案 1 :(得分:286)

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

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

来自文档:

  

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

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

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

答案 2 :(得分:177)

您可以使用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

答案 3 :(得分:60)

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

  

无法停止或中断 除了forEach()循环   通过抛出异常。如果您需要此类行为,.forEach()方法是 错误工具 ,请使用普通循环。如果要测试谓词的数组元素并需要布尔返回值,则可以改为使用every()some()

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

  

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

答案 4 :(得分:56)

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

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

答案 5 :(得分:23)

考虑使用jquery的{​​{1}}方法,因为它允许在回调函数内返回false:

each

Lodash库还提供takeWhile方法,可以使用map / reduce / fold等链接:

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

答案 6 :(得分:21)

var array = [1,2,3,4];

for(var item of array){
    console.log(item);
    if(item == 2){
       break;
    }
}

答案 7 :(得分:17)

从您的代码示例中,Array.prototype.find看起来就像您要找的那样:Array.prototype.find()Array.prototype.findIndex()

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

答案 8 :(得分:14)

如果您想使用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函数才会更新。如果它不存在,则不会修改它。

答案 9 :(得分:8)

简短回答:为此使用for...break或更改代码以避免中断forEach。请勿使用.some().every()来模拟for...break。重写代码以避免for...break循环,或使用for...break。每次你使用这些方法作为for...break替代神杀死小猫。

答案很长:

.some().every()都返回boolean值,.some()返回true,如果有任何元素为其传递函数返回true,则每个如果传递函数返回false的任何元素,则返回false。这就是这个功能的意思。使用函数来表示它们并不意味着比使用表格而不是CSS更糟糕,因为它会让每个读取代码的人感到沮丧。

此外,将这些方法用作for...break替代方法的唯一可能方法是产生副作用(更改.some()回调函数之外的某些变量),这与{{{ 1}}。

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

您始终可以重写代码,以便for...break不需要。您可以使用for...break过滤数组,可以使用.filter()拆分数组,依此类推,然后使用.slice().forEach()作为数组的这一部分。

答案 10 :(得分:4)

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

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/

答案 11 :(得分:3)

如果你不需要在迭代后访问你的数组,你可以通过将数组的长度设置为0来纾困。如果你在迭代后仍然需要它,你可以使用切片克隆它..

[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;
});

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

答案 12 :(得分:3)

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

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

答案 13 :(得分:3)

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

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将导致中断。如果这实际上没有用,请告诉我。

答案 14 :(得分:2)

如前所述,您无法中断.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!

链接

答案 15 :(得分:2)

我提出的另一个概念:

function forEach(array, cb) {
  var shouldBreak;
  function _break() { shouldBreak = true; }
  for (var i = 0, bound = array.length; i < bound; ++i) {
    if (shouldBreak) { break; }
    cb(array[i], i, array, _break);
  }
}

// Usage

forEach(['a','b','c','d','e','f'], function (char, i, array, _break) {
  console.log(i, char);
  if (i === 2) { _break(); }
});

答案 16 :(得分:1)

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

此示例使用匿名函数在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;
        }
    });
})();
<div id="printed-result"></div>

我的两分钱。

答案 17 :(得分:1)

我知道这不是正确的方法。它不会打破循环。 这是一个Jugad

&#13;
&#13;
let result = true;
[1, 2, 3].forEach(function(el) {
    if(result){
      console.log(el);
      if (el === 2){
        result = false;
      }
    }
});
&#13;
&#13;
&#13;

答案 18 :(得分:1)

另一种方法

"HTTP/1.1 200 [\r][\n]"
"X-Content-Type-Options: nosniff[\r][\n]"
"X-XSS-Protection: 1; mode=block[\r][\n]"
"Cache-Control: no-cache, no-store, max-age=0, must-revalidate[\r][\n]"
"Pragma: no-cache[\r][\n]"
"Expires: 0[\r][\n]"
"X-Frame-Options: DENY[\r][\n]"
"X-Content-Type-Options: nosniff[\r][\n]"
"X-Content-Type-Options: nosniff[\r][\n]"
"X-Content-Type-Options: nosniff[\r][\n]"
"Cache-Control: no-cache, no-store, max-age=0, must-revalidate[\r][\n]"
"Pragma: no-cache[\r][\n]"
"Expires: 0[\r][\n]"
"Connection: close[\r][\n]"
"Transfer-Encoding: chunked[\r][\n]"
"Date: Wed, 03 Oct 2018 00:31:53 GMT[\r][\n]"
"Content-Type: application/json;charset=UTF-8[\r][\n]"
"[\r][\n]"
"{"customerNumber":"1000000030"}" <- MISSING \r\n

答案 19 :(得分:1)

我为此目的使用 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
}
//

答案 20 :(得分:0)

您可以创建forEach的变体,该变体允许breakcontinuereturn甚至async / await:(用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);
}

答案 21 :(得分:0)

通过减少数组长度来做一个简单的数组拼接怎么样???

它会弄乱您当前的数组,因此您必须使用解构功能制作副本...

例如:

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

// Don't forget to work with a copy of the original array otherwise you will loose your data contained inside
[...arr].forEach(function(value, index, copiedArrInstance) {
  if (index === 2) copiedArrInstance.length = 0;
  console.log(value);
});

// you will still have your array intact
console.log(arr);

答案 22 :(得分:0)

尝试“查找”:

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" }

答案 23 :(得分:0)

为什么不尝试将函数包装在 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);
});

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

答案 24 :(得分:0)

如果您需要根据情况中断数组中元素的值(即中断条件不取决于在分配数组元素值后可能会更改的运行时变量),则可以还可以按以下方式使用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中查看

希望它可以帮助某人。

答案 25 :(得分:0)

我更喜欢使用for in

var words = ['a', 'b', 'c'];
var text = '';
for (x in words) {
    if (words[x] == 'b') continue;
    text += words[x];
}
console.log(text);

for in的工作原理与forEach非常相似,您可以在里面添加return函数。也有更好的表现。

答案 26 :(得分:0)

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

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

答案 27 :(得分:0)

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

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

答案 28 :(得分:0)

同意@bobince,upvoted。

另外,仅供参考:

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

答案 29 :(得分:0)

使用array.prototype.every函数,它为您提供了打破循环的实用程序。请参阅此处示例Javascript documentation on Mozilla developer network

答案 30 :(得分:-1)

您可以使用includes

console.log(
  [1, 2, 3].includes(1),
  [1, 2, 3].includes(4)
)

这是ES2016+ feature。如果不支持,则可以使用this polyfill

答案 31 :(得分:-1)

之前,我的代码在下面

 this.state.itemsDataSource.forEach((item: any) => {
                if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {
                    //BDialogHelper.show(this.state.context, 'Please enter inoice number or  date (' + item.itemName + ' )', DialogType.WARNING, DialogResponseStyle.OK);
                    this.showStatusMessage('Please enter inoice number or  date (' + item.itemName + ' )');
                    return false;
                }
            });

我已更改为以下内容,已修复。

 for (var i = 0; i < this.state.itemsDataSource.length; i++) {
                var item = this.state.itemsDataSource[i];
                if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {
                    BDialogHelper.show(this.state.context, 'Please enter inoice number or  date (' + item.itemName + ' )', DialogType.WARNING, DialogResponseStyle.OK);
                    //this.showStatusMessage('Please enter inoice number or  date (' + item.itemName + ' )');
                    return false;
                }
            }

答案 32 :(得分:-2)

validates :tag_id, :uniqueness => {:scope => :thing_id}

答案 33 :(得分:-3)

如果您真的爱forEach,我认为可以这样模仿continue

arr.forEach(x=> {
  if(x>4) return;
  // some heavy code down below
});

但是,即使您喜欢forEach,也不要使用黑客来模仿break-只需使用常规的for循环即可。 实际上,我想出了聪明的解决方案,但是要知道它不是很好,因为它是 mutant 输入。如果您完全不介意更改提供的数据,则可以使用它,但是在大多数情况下,您介意。

arr.forEach(x=> {
  if (x>4) {arr.length=0; return;}
  // some heavy code might be here
});

答案 34 :(得分:-3)

地图解决方案与OP示例非常相似。关于性能, for循环是最快的,但 map forEach 一样简洁,而对于“没有太多项目”则不会有所作为。

[1,2,3].map(function(el) {
    if(el === 1) return
})

答案 35 :(得分:-3)

是的,可以继续并退出forEach循环。

要继续,可以使用return,循环将继续,但当前函数将结束。

要退出循环,可以将第三个参数设置为0长度,设置为空数组。循环不会继续,当前函数会继续,因此您可以使用“返回”完成操作,就像在正常的for循环中退出...

此:

[1,2,3,4,5,6,7,8,9,10].forEach((a,b,c) => {
    console.log(a);
    if(b == 2){return;}
    if(b == 4){c.length = 0;return;}
    console.log("next...",b);
});

将打印以下内容:

1
next... 0
2
next... 1
3
4
next... 3
5