[小菜一碟] 在 JavaScript 的 Array.prototype.forEach() 方法實現 continue; 及 break; 的效果

眾所皆知,在 JavaScript 中的 Array 有一個 forEach() 方法很好用(而且 IE 9 以上有支援),大部分的使用情境都可以用來取代傳統的 for 語法,但難免會有需要 continue 或 break 的場景,continue;break; 在 forEach() 方法中是不會有作用的,我們需要換個方式來做。

首先,我們先看一下 forEach() 方法 Polyfill 的原始碼,先了解整個 forEach() 方法的實作邏輯,從這裡面我們可以知道,forEach() 方法的內部實際上是跑一個 while 迴圈,而不是 for 迴圈。

continue

在了解了整個 forEach() 方法的實作邏輯之後,如果我們要實現 continue; 的效果,其實只要在 callback function 裡面加上 return 即可。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

array.forEach(function (item, index) {
    if (item >= 2 && item <= 7) {
        return;
    }

    console.log(item);
});

// 印出:
// 1
// 8
// 9

break

但是要實現 break; 就沒那麼容易了,這等同於是要在 while 迴圈中 break;,而我們又只能在 callback function 裡面下手,所以只得繞道而行,底下提供三種方式給大家參考。

Array.prototype.splice()

第一種方法我們要借助 Array.prototype.splice() 方法,splice() 方法有個特性,就是它會去異動到記憶體中實體的陣列,所以我們只要在需要 break 的地方,將陣列中後面的元素移除掉,讓 while 迴圈取不到元素,就達到了 break; 的效果。

但是用 splice() 要非常小心,所有參考到這個實體陣列的變數也都會受到影響,所以我們需要建立另一個擁有相同元素的陣列來執行 forEach(),在 JavaScript 中方法有很多,而我選用 Array.from() 來幫我建立相同元素的陣列。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

Array.from(array).forEach(function (item, index, arr) {
    if (item === 4) {
        arr.splice(index, arr.length - index);
        return;
    }

    console.log(item);
});

// 印出:
// 1
// 2
// 3

throw exception

第二種方法比較暴力一點,直接在需要 break 的地方吐 Exception,讓程式強制中斷,也能達到 break; 的效果。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

try {
    array.forEach(function (item, index) {
        if (item === 4) {
            throw {};
        }

        console.log(item);
    });
} catch { }

// 印出:
// 1
// 2
// 3

可以把它做成一個 tryForEach() 方法,放在 Array.prototype 裡面。

if (!Array.prototype.tryForEach) {
    Array.prototype.tryForEach = function (callback, thisArg) {
        try {
            this.forEach(callback, thisArg);
        } catch { }
    }
}

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

array.tryForEach(function (item, index) {
    if (item === 4) {
        throw {};
    }

    console.log(item);
});

// 印出:
// 1
// 2
// 3

空轉

第三種方法是讓 while 迴圈空轉,我們只要加一個 break flag,在需要 break 的地方將 break flag 標記為 true,此後的循環全部 return 都不執行,以此達到 break; 的效果。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

array.forEach(function (item, index) {
    if (this.breakFlag === true) return;

    if (item === 4) {
        this.breakFlag = true;
        return;
    }

    console.log(item);
});

// 印出:
// 1
// 2
// 3

或許可以用 some()

上面那三種在 forEach() 實現 break; 的方法,就提供給有需要朋友參考,我們來聊聊另外一個方法 - Array.prototype.some(),它相當於 C# 中的 Any(),雖然語意上它不是用來實現 break; 效果的,但是我個人認為,如果我們不想用 for 語法又有 break 的需求,用 some() 是挺適合的。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

array.some(function (item, index) {
    if (item === 4) {
        return true;
    }

    console.log(item);
});

// 印出:
// 1
// 2
// 3

最後,如果我們對 JavaScript 裡面的 for 語法還停留在 for (var i = 0; i < length; i++) { } 這個印象的朋友,其實 JavaScript 還有兩個 for 語法:for...infor...of(IE no support),也提供給各位朋友參考。

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

for (var index in array) {
    if (array.hasOwnProperty(index)) {
        var item = array[index];
        
        if (item === 4) {
            break;
        }

        console.log(item);
    }
}

// IE no support
for (var item of array) {
    if (item === 4) {
        break;
    }

    console.log(item);
}

// 皆印出:
// 1
// 2
// 3

參考資料

C# 指南 ASP.NET 教學 ASP.NET MVC 指引
Azure SQL Database 教學 SQL Server 教學 Xamarin.Forms 教學