译者按: 鲁迅曾经说过,学习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/