在 JavaScript 棄用 For 迴圈,擁抱 Reduce、ForEach、Filter、Map

在 ES5 其實就有這些功能了,但是到現在還是可以看到很多人都仰賴 for 迴圈來做事,然後在迴圈外面先建立一個 newArray 或是暫存陣列,用來處理新陣列、物件的內容,接著要處理其他的陣列,一堆千奇百怪的寫法又會再出現一次,所以這次乾脆寫篇文章來提醒大家好了。

雖然用上 for 迴圈是沒有什麼不好,不過看看最近大家都在撰寫函數式程式,在函數式程式中並沒有 for 迴圈,而是用 reducemapfilter 這樣的函式代替,這聽起來可能讓你感到不知所措,不過不覺得很有趣嗎?畢竟 for 迴圈通常都拿來掃描陣列或物件的,這樣的情況下用上 map 總比寫個 for(var i=0; i<arr.length; i++) 簡單太多了。

這些函式怎麼協助我替代 for 迴圈?

這裡先把每個函式都先說清楚一遍,這樣的話你可能就知道哪些函式可以用來替代什麼時機的 for 迴圈了。

  • forEach:遍歷每個元素。
  • map:遍歷每個元素,回傳的值會替代原本陣列內的值。
  • filter:遍歷每個元素,回傳 true 時,目前的值會保留在陣列內,這會回傳一個新陣列,而不是修改原本的陣列。
  • reduce:遍歷每個元素,依序組合、加總,然後丟給下個元素,最終會回傳一個結果。

如果你還是不清楚的話,沒關係,下面都有範例,畢竟沒看到範例之前你可能還是不知道那個奇怪的 reduce 是什麼東西。

ForEach

如果你沒有打算要修改陣列的內容,你只是希望遍歷陣列內的每個元素,那麼你就可以使用 forEach 函式。在傳統 for 迴圈你會這麼做。

var arr = [1, 2, 3];

for(var i = 0; i < arr.length; i++) {  
    console.log(arr[i]);
}

接著讓我們透過 forEach 並搭配 () => {} 來讓你的程式更加簡短但是達到跟上面相同效果。

var arr = [1, 2, 3];

arr.forEach((val) => {  
    console.log(arr[i]);
})

如果你不知道 () => {} 是什麼,它實際上就是 ES6 推出的函式簡寫,也就是 function() {} 的縮寫(雖然有點不同,但就不提了)。

Map

如果你希望遍歷陣列內的每個內容,然後修改原始陣列,那麼 map 就是你的好夥伴。先看看以往的做法是如何。

var arr    = [1, 2, 3],  
    newArr = [];

for(var i = 0; i < arr.length; i++) {  
    newArr[i] = arr[i] * 2;
}

console.log(newArr); // [2, 4, 6]  

接著透過 map 你的程式可以短個至少 40% 以上。

var arr = [1, 2, 3];

arr.map((val) => {  
    return val * 2;
})

console.log(arr); // [2, 4, 6]  

Filter

這個按照字面上的意思來說就是過濾器,filter 不會修改值,但他會幫你決定要不要將這個值留在陣列裡面,要注意的是 filter回傳一個新的陣列,而不是修改原本的陣列。

var arr    = [1, 2, 3, 4],  
    newArr = [];

for(var i = 0; i < arr.length; i++) {  
    if(arr[i] > 2) {
        newArr.push(arr[i]);
    }
}

console.log(newArr); // [3, 4]  

透過 filter,在遍歷陣列內容的同時,你可以回傳布林值來決定要不要將這個值留在陣列內。

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

var newArr = arr.filter((val) => {  
    return val > 2;
})

console.log(newArr); // [3, 4]  

Reduce

如果你希望把陣列內的內容作加總並最終回傳一個結果,那麼你就可以用上 reduce。這個使用的機率應該不高,但是要將陣列內的內容全部統整的時候應該很好用。

var arr      = [1, 2, 3],  
    totalNum = 0;

for(var i = 0; i < arr.length; i++) {  
    total += arr[i];
}

console.log(totalNum); // 6  

接著透過 reduce 你就可以直接取得加總的結果。

var arr = [1, 2, 3];

var totalNum = arr.reduce((total, val) => {  
    return total + val;
}, 0);

console.log(totalNum); // 6  

在上述的 reduce 中的第二個參數為 0 即是將 total 初始化成 0,就像我們在傳統程式中定義 var totalNum = 0; 一樣。然後我們會取得每個元素的值,對 total 進行加總,最終 reduce 就會回傳這個值。


將函式串連在一起

你能夠將這些函式串連在一起,這樣就不用寫一堆變數儲存他們的結果然後丟給另一個函式進行處理。實際程式像下面這樣。

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

var newArr = arr.filter((val) => {  
    return val > 2;
}).map((val) => {
    return val * 2;
})

console.log(newArr); // [6, 8]  

CoffeeScript 能讓這更加簡潔

如果你覺得上面串連的程式碼還不夠簡潔,那麼你可以試試看 CoffeeScript,然後你就可以寫成這樣。

arr = [1, 2, 3, 4]

newArr = arr.filter (val) ->  
    val > 2
.map (val) ->
    val * 2

console.log newArr # [6, 8]  

如果你有興趣透過 JavaScript 撰寫函數式程式(像 HaskellElm 那樣),那麼你就可以考慮 CoffeeScript


圖示來源:Loop by useiconic.com from the Noun Project