譯者按: 魯迅曾經說過,學習JavaScript最好方式莫過於敲代碼了!數組
原文: Master Map & Filter, Javascript’s Most Powerful Array Functionsapp
譯者: Fundebug函數
爲了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原做者全部,翻譯僅用於學習。oop
這篇文章面向那些已經熟練使用for循環,但對Array.map和Array.filter並無特別理解的開發者。本文將會手把手去實現這兩個函數,來深刻理解它們的工做原理。學習
Array.map經過對輸入的數組中每個元素進行變換,返回由變換後的元素按序組成的新數組。原始數組的值不會被修改。假設咱們相對一個數組中的每個元素乘以3,使用for循環能夠這樣寫。優化
var originalArr = [1, 2, 3, 4, 5]; var newArr = []; for(var i = 0; i < originalArr.length; i++) { newArr[i] = originalArr[i] * 3; } console.log(newArr); // -> [3, 6, 9, 12, 15]
接下來咱們將這個for循環抽象成一個函數。編碼
var originalArr = [1, 2, 3, 4, 5]; function multiplyByThree(arr) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = arr[i] * 3; } return newArr; } var arrTransformed = multiplyByThree(originalArr); console.log(arrTransformed); // -> [3, 6, 9, 12, 15]
如今咱們繼續深化這個抽象思路,將multiplyByThree中對每個元素乘以3部分抽象爲一個新的函數。翻譯
var originalArr = [1, 2, 3, 4, 5]; function timesThree(item) { return item * 3; } function multiplyByThree(arr) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = timesThree(arr[i]); } return newArr; } var arrTransformed = multiplyByThree(originalArr); console.log(arrTransformed); // -> [3, 6, 9, 12, 15]
這樣有什麼好處呢?設想若是咱們想對每個元素乘以5,或則10,咱們還要把整個for循環寫一遍嗎! 若是咱們對timesThree函數稍做修改,就能夠輕鬆的複用不少代碼。debug
咱們將:code
function multiplyByThree(arr) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = timesThree(arr[i]); } return newArr; }
重構爲:
function multiply(arr, multiplyFunction) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = multiplyFunction(arr[i]); } return newArr; }
咱們將multiplyByThree重命名爲multiply,並增長了一個參數。該參數是一個函數,定義了數組元素的變換規則。經過定義一個timesThree函數來達到實現對每個數組元素乘以3的目的。
var originalArr = [1, 2, 3, 4, 5]; function timesThree(item) { return item * 3; } var arrTimesThree = multiply(originalArr, timesThree); console.log(arrTimesThree); // -> [3, 6, 9, 12, 15]
有何優勢呢?咱們能夠很簡單定義任何變換:
var originalArr = [1, 2, 3, 4, 5]; function timesFive(item) { return item * 5; } var arrTimesFive = multiply(originalArr, timesFive); console.log(arrTimesFive); // -> [5, 10, 15, 20, 25]
咱們進一步抽象:
function multiply(arr, multiplyFunction) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = multiplyFunction(arr[i]); } return newArr; }
將multiply改成map, multiplyFunction改成transform:
function map(arr, transform) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = transform(arr[i]); } return newArr; }
咱們能夠將任何對單個元素操做的函數傳入map函數。好比,咱們將全部字符都變換成大寫:
function makeUpperCase(str) { return str.toUpperCase(); } var arr = ['abc', 'def', 'ghi']; var ARR = map(arr, makeUpperCase); console.log(ARR); // -> ['ABC', 'DEF, 'GHI']
咱們定義的map函數和原生的Array.map仍是有區別的:數組再也不須要做爲第一個參數傳入,而是在點(.)的左側。若是使用咱們定義的map函數,以下:
function func(item) { return item * 3; } var arr = [1, 2, 3]; var newArr = map(arr, func); console.log(newArr); // -> [3, 6, 9]
將其改寫爲使用Array.map函數的形式:
function func(item) { return item * 3; } var arr = [1, 2, 3]; var newArr = arr.map(func); console.log(newArr); // -> [3, 6, 9]
除了變換函數外,Array.map還能夠接收其它兩個參數: 數組索引(index), 原始的數組。
function logItem(item) { console.log(item); } function logAll(item, index, arr) { console.log(item, index, arr); } var arr = ['abc', 'def', 'ghi']; arr.map(logItem); // -> 'abc', 'def', 'ghi' arr.map(logAll); // -> 'abc', 0, ['abc', 'def', 'ghi'] // -> 'def', 1, ['abc', 'def', 'ghi'] // -> 'ghi', 2, ['abc', 'def', 'ghi']
所以,你能夠再變換函數中使用索引和原始的數組。好比:你想要將一個列表變爲帶序號的列表,則須要使用索引(index)參數:
function multiplyByIndex(item, index) { return (index + 1) + '. ' + item; } var arr = ['bananas', 'tomatoes', 'pasta', 'protein shakes']; var mappedArr = arr.map(multiplyByIndex); console.log(mappedArr); // -> // ["1. bananas", "2. tomatoes", "3. pasta", "4. protein shakes"]
所以,咱們本身實現的map函數也應該支持這兩個參數:
function map(arr, transform) { var newArr = []; for(var i = 0; i < arr.length; i++) { newArr[i] = transform(arr[i], i, arr); } return newArr; }
固然,Array.map函數還有一些錯誤檢查和執行優化的代碼,咱們定義的map只編碼了核心功能。
Array.filter將數組中不知足條件的元素過濾,咱們能夠用for循環加上Array.push來實現。
下面這段JS代碼將全部大於5的元素篩選出來:
var arr = [2, 4, 6, 8, 10]; var filteredArr = []; for(var i = 0; i < arr.length; i++) { if(arr[i] > 5) { filteredArr.push(arr[i]); } } console.log(filteredArr); // -> [6, 8, 10]
咱們能夠抽象這段代碼,定義爲一個函數:
function filterLessThanFive(arr) { var filteredArr = []; for(var i = 0; i < arr.length; i++) { if(arr[i] > 5){ filteredArr.push(arr[i]); } } return filteredArr; } var arr1 = [2, 4, 6, 8, 10]; var arr1Filtered = filterLessThanFive(arr1); console.log(arr1Filtered); // -> [6, 8, 10]
進一步抽象,將過濾條件抽出來:
function isGreaterThan5(item) { return item > 5; } function filterLessThanFive(arr) { var filteredArr = []; for(var i = 0; i < arr.length; i++) { if(isGreaterThan5(arr[i])) { filteredArr.push(arr[i]); } } return filteredArr; } var originalArr = [2, 4, 6, 8, 10]; var newArr = filterLessThanFive(originalArr); console.log(newArr); // -> [6, 8, 10]
將過濾條件函數做爲參數傳入:
function filterBelow(arr, greaterThan) { var filteredArr = []; for(var i = 0; i < arr.length; i++) { if(greaterThan(arr[i])) { filteredArr.push(arr[i]); } } return filteredArr; } var originalArr = [2, 4, 6, 8, 10];
大功告成!咱們可使用以下代碼來取出全部大於5的元素:
function isGreaterThan5(item) { return item > 5; } var newArr = filterBelow(originalArr, isGreaterThan5); console.log(newArr); // -> [6, 8, 10];
咱們將filterBelow重命名爲filter, greaterThan重命名爲testFunction:
function filter(arr, testFunction) { var filteredArr = []; for(var i = 0; i < arr.length; i++) { if(testFunction(arr[i])) { filteredArr.push(arr[i]); } } return filteredArr; }
這就是一個基本的Array.filter函數了!
var arr = ['abc', 'def', 'ghijkl', 'mnopuv']; function longerThanThree(str) { return str.length > 3; } var newArr1 = filter(arr, longerThanThree); var newArr2 = arr.filter(longerThanThree); console.log(newArr1); // -> ['ghijkl', 'mnopuv'] console.log(newArr2); // -> ['ghijkl', 'mnopuv']
一樣,Array.filter也有索引(index)和原始數組這兩個額外參數。
function func(item, index, arr) { console.log(item, index, arr); } var arr = ['abc', 'def', 'ghi']; arr.filter(func); // -> 'abc', 0, ['abc', 'def', 'ghi'] // -> 'def', 1, ['abc', 'def', 'ghi'] // -> 'ghi', 2, ['abc', 'def', 'ghi']
版權聲明:
轉載時請註明做者Fundebug以及本文地址:
https://blog.fundebug.com/2017/07/26/master_map_filter_by_hand_written/