繁体   English   中英

短路 Array.forEach 就像调用 break

[英]Short circuit Array.forEach like calling break

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

如何使用 JavaScript 中的新forEach方法来做到这一点? 我试过return; return false; break break crash 和return只会继续迭代。

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

 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 异常并不是非常漂亮。 如果您确实需要在其中break ,则传统for循环可能更合适。

使用Array#some

相反,使用Array#some

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

这是可行的,因为一旦以数组顺序执行的任何回调返回truesome回调就会返回true ,从而缩短其余部分的执行。

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

现在在 ECMAScript2015(又名 ES6)中使用新的 for of loop有更好的方法来做到这一点。 例如,此代码不打印数字 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...infor...of语句都会迭代某些内容。 它们之间的主要区别在于它们迭代的内容。 for...in语句以原始插入顺序迭代对象的可枚举属性。 for...of语句迭代可迭代对象定义要迭代的数据。

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

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

您可以使用每种方法:

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

更多细节在这里

引用Array.prototype.forEach()的 MDN 文档

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

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

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

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

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

从您的代码示例中,您正在寻找Array.prototype.findArray.prototype.find()Array.prototype.findIndex()

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

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

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

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

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');
// => []

如果您想使用Dean Edward 的建议并抛出 StopIteration 错误以跳出循环而不必捕获错误,您可以使用以下函数( 最初来自此处):

// 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 函数已经存在,它只会更新它。 如果它不存在,它不会修改它。

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

长答案:

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

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

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

你总是可以重写你的代码,这样就不需要for...break 您可以使用.filter()过滤数组,可以使用.slice()等拆分数组,然后对数组的那一部分使用.forEach().map()

如前所述,您不能破坏.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!

链接

我提出的另一个概念:

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

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

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

如果您不需要在迭代后访问您的数组,您可以通过将数组的长度设置为 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;
});

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

在另一个网站上找到了这个解决方案。 您可以将 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/

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

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

尝试“查找”:

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

还有一种方法:

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

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

使用array.prototype.every函数,它为您提供打破循环的实用程序。 在此处查看示例Mozilla 开发人员网络上的 Javascript 文档

同意@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

如果你想保持你的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>

我的两分钱。

我更喜欢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到exit函数。 性能也更好。

如果您需要根据您的情况下已经在数组中的元素的值进行中断(即,如果中断条件不依赖于在数组被分配其元素值后可能发生变化的运行时变量),您也可以使用组合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
});

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

JSFiddle中查看

希望它可以帮助某人。

以前,我的代码在下面

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

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

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

通过减少数组长度来进行简单的数组拼接怎么样???

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

例如:

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

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

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

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

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

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

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

如果你想打破一个 foreche 循环,使用这样的标志布尔值<\/strong>:

let flag = true;
My_list.forEach((item,index)=>{
     if(flag && (index === 2)){
         flag = false;
        }
});

flag = true;

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

这里要注意的关键是使用语句return to 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

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

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

您可以创建一个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);
}
[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

我如何使用 JavaScript 中的新forEach方法来做到这一点? 我试过return; return false; break break崩溃, return除了继续迭代什么都不做。

如果它不符合您的条件,您可以在 forEach 内的回调函数中执行空白返回。 例如,假设我们要打印数组中所有小于 3 的值,当 forEach 到达 3 时,它将中断。

function breakAtThree() {
    [1, 2, 3].forEach(function (el) {
        el < 3 ? console.log(el) : null;
    });
}
breakAtThree()

我使用return false它对我有用。

如果您真的爱forEach我认为可以continue模仿。

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

但是,即使你崇拜forEach不使用黑客来模拟break -只要使用正规的for循环。 实际上,我想出了一个聪明的解决方案,但是请注意,由于它是mutate输入,因此效果不佳 如果您完全不介意更改提供的数据,则可以使用它,但是在大多数情况下,您都可以考虑。

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

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);
[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

如何使用JavaScript中新的forEach方法做到这一点? 我试过return; return false; break break崩溃并return除了继续迭代外什么也没有做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM