當(dāng)年懵懂無知的我被問到這個問題時鸣峭,腦袋一片空白,因?yàn)槲乙欢日J(rèn)為forEach
可能只是為了方便書寫所創(chuàng)造出來的語法糖,在業(yè)務(wù)代碼中也經(jīng)常使用竟闪,但沒有思考過它存在的問題,本文旨在記錄自己的心路歷程杖狼,拋磚引玉炼蛤,如果對你有所幫助那就更好啦。
那么回到標(biāo)題蝶涩,首先forEach
是不能使用任何手段跳出循環(huán)的理朋,為什么呢絮识?繼續(xù)往下看。
我們知道forEach
接收一個函數(shù)嗽上,它一般有兩個參數(shù)次舌,第一個是循環(huán)的當(dāng)前元素,第二個是該元素對應(yīng)的下標(biāo)兽愤,手動實(shí)現(xiàn)一下偽代碼:
Array.prototype.myForEach?=?function?(fn)?{
????for?(let?i?=?0;?i?<?this.length;?i++)?{
????????fn(this[i],?i,?this);
????}}
forEach
是不是真的這么實(shí)現(xiàn)我無從考究彼念,但是以上這個簡單的偽代碼確實(shí)滿足forEach
的特性,而且也很明顯就是不能跳出循環(huán)烹看,因?yàn)楦緵]有辦法操作到真正的for
循環(huán)體国拇。
后來經(jīng)過查閱文檔,發(fā)現(xiàn)官方對forEach
的定義根本不是我認(rèn)為的語法糖惯殊,它的標(biāo)準(zhǔn)說法是forEach
為每個數(shù)組元素執(zhí)行一次你所提供的函數(shù)酱吝。官方文檔也有這么一段話:
除拋出異常之外,沒有其他方法可以停止或中斷循環(huán)土思。如果您需要這種行為务热,則該forEach()方法是錯誤的工具。
使用拋出異常來跳出foreach循環(huán):
let?arr?=?[0,?1,?"stop",?3,?4];
try?{
????arr.forEach(element?=>?{
????????if?(element?===?"stop")?{
????????????throw?new?Error("forEachBreak");
????????}
????????console.log(element);?//?輸出?0?1?后面不輸出
????});
}?catch?(e)?{
????console.log(e.message);?//?forEachBreak};
那么可不可以認(rèn)為己儒,forEach
可以跳出循環(huán)崎岂,使用拋出異常就可以了?這點(diǎn)我認(rèn)為仁者見仁智者見智吧闪湾,在forEach
的設(shè)計中并沒有中斷循環(huán)的設(shè)計冲甘,而使用try-catch
包裹時,當(dāng)循環(huán)體過大性能會隨之下降途样,這是無法避免的江醇,所以拋出異常可以作為一種中斷forEach
的手段何暇,但并不是為解決forEach
問題而存在的銀彈陶夜。
再次回歸到開頭寫的那段偽代碼,對它進(jìn)行一些優(yōu)化裆站,在真正的for循環(huán)中加入對傳入函數(shù)的判斷:
//?為避免爭議此處不覆寫原有forEach函數(shù)
Array.prototype.myForEach?=?function?(fn)?{
????for?(let?i?=?0;?i?<?this.length;?i++)?{
????????let?ret?=?fn(this[i],?i,?this);
????????if?(typeof?ret?!==?"undefined"?&&?(ret?==?null?||?ret?==?false))?break;
????}}
這樣的話就能根據(jù)return
值來進(jìn)行循環(huán)跳出啦:
let?arr?=?[0,?1,?"stop",?3,?4];
arr.myForEach(x?=>?{
????if?(x?===?'stop')?return?false
????console.log(x);?//?輸出?0?1?后面不輸出
});
//?return即為continue:
arr.myForEach(x?=>?{
????if?(x?===?'stop')?return
????console.log(x);?//?0?1?3?4});
文檔中還提到forEach需要一個同步函數(shù)条辟,也就是說在使用異步函數(shù)或Promise
作為回調(diào)時會發(fā)生預(yù)期以外的結(jié)果,所以forEach
還是需要慎用宏胯。
當(dāng)然羽嫡,用簡單的for
循環(huán)去完成一切事情也不失為一種辦法,代碼首先是寫給人看的肩袍,附帶在機(jī)器上運(yùn)行的作用杭棵,forEach
在很多時候用起來更加順手,但也務(wù)必在理解JS如何設(shè)計這些工具函數(shù)的前提下來編寫我們的業(yè)務(wù)代碼了牛。
我們可以在遍歷數(shù)組時使用for..of..
颜屠,在遍歷對象時使用for..in..
,而官方也在forEach
文檔下列舉了其它一些工具函數(shù)鹰祸,這里不做過多展開:
Array.prototype.find()
Array.prototype.findIndex()
Array.prototype.map()
Array.prototype.filter()
Array.prototype.every()
Array.prototype.some()
如何根據(jù)不同的業(yè)務(wù)場景甫窟,選擇使用對應(yīng)的工具函數(shù)來更有效地處理業(yè)務(wù)邏輯,才是我們真正應(yīng)該思考的蛙婴,或許這也是面試當(dāng)中真正想考察的吧粗井。