面試官問我 JS 中 foreach 能不能跳出迴圈

當年懵懂無知的我被問到這個問題時,腦袋一片空白,因為我一度認為

forEach

可能只是為了方便書寫所創造出來的語法糖,在業務程式碼中也經常使用,但沒有思考過它存在的問題,本文旨在記錄自己的心路歷程,拋磚引玉,如果對你有所幫助那就更好啦。

那麼回到標題,首先

forEach

不能使用任何手段跳出迴圈

的,為什麼呢?繼續往下看。

我們知道

forEach

接收一個函式,它一般有兩個引數,第一個是迴圈的當前元素,第二個是該元素對應的下標,手動實現一下虛擬碼:

Array。prototype。myForEach = function (fn) {    for (let i = 0; i < this。length; i++) {        fn(this[i], i, this);    }}

forEach

是不是真的這麼實現我無從考究,但是以上這個簡單的虛擬碼確實滿足

forEach

的特性,而且也很明顯就是不能跳出迴圈,因為根本沒有辦法操作到真正的

for

迴圈體。

後來經過查閱文件,發現官方對

forEach

的定義根本不是我認為的語法糖,它的標準說法是

forEach

為每個陣列元素執行一次

你所提供的函式。官方文件也有這麼一段話:

除丟擲異常之外,沒有其他方法可以停止或中斷迴圈。如果您需要這種行為,則該forEach()方法是錯誤的工具。

使用丟擲異常來跳出foreach迴圈:

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

那麼可不可以認為,

forEach

可以跳出迴圈,使用丟擲異常就可以了?這點我認為仁者見仁智者見智吧,在

forEach

的設計中並沒有中斷迴圈的設計,而使用

try-catch

包裹時,當

迴圈體過大效能會隨之下降

,這是無法避免的,所以丟擲異常可以作為一種中斷

forEach

的手段,但並不是為解決

forEach

問題而存在的銀彈。

再次迴歸到開頭寫的那段虛擬碼,對它進行一些最佳化,在真正的for迴圈中加入對傳入函式的判斷:

// 為避免爭議此處不覆寫原有forEach函式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;    }}

這樣的話就能根據

return

值來進行迴圈跳出啦:

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

需要一個同步函式

,也就是說在使用

非同步函式

Promise

作為回撥時會發生預期以外的結果,所以

forEach

還是需要慎用。

當然,用簡單的

for

迴圈去完成一切事情也不失為一種辦法,

程式碼首先是寫給人看的,附帶在機器上執行的作用

forEach

在很多時候用起來更加順手,但也務必在理解

JS

如何設計這些工具函式的前提下來編寫我們的業務程式碼。

我們可以在遍歷陣列時使用

for。。of。。

,在遍歷物件時使用

for。。in。。

,而官方也在

forEach

文件下列舉了其它一些工具函式,這裡不做過多展開:

Array。prototype。find()Array。prototype。findIndex()Array。prototype。map()Array。prototype。filter()Array。prototype。every()Array。prototype。some()

如何根據不同的業務場景,選擇使用對應的工具函式來更有效地處理業務邏輯,才是我們真正應該思考的,或許這也是面試當中真正想考察的吧。