资讯专栏INFORMATION COLUMN

如何实现JavaScript的Map和Filter函数?

bawn / 904人阅读

摘要:本文将会手把手去实现这两个函数,来深入理解它们的工作原理。通过对输入的数组中每一个元素进行变换,返回由变换后的元素按序组成的新数组。函数我们将重构为我们将重命名为,并增加了一个参数。通过定义一个函数来达到实现对每一个数组元素乘以的目的。

译者按: 鲁迅曾经说过,学习JavaScript最好方式莫过于敲代码了!

原文: Master Map & Filter, Javascript’s Most Powerful Array Functions

译者: Fundebug

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

这篇文章面向那些已经熟练使用for循环,但对Array.map和Array.filter并没有特别理解的开发者。本文将会手把手去实现这两个函数,来深入理解它们的工作原理。

Array.map

Array.map通过对输入的数组中每一个元素进行变换,返回由变换后的元素按序组成的新数组。原始数组的值不会被修改。假设我们相对一个数组中的每一个元素乘以3,使用for循环可以这样写。

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循环抽象成一个函数。

multiplyByThree函数
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函数稍作修改,就可以轻松的复用很多代码。

multiply函数

我们将:

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]
Map

我们进一步抽象:

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"]
Array.map

我们定义的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]
Arrary.map参数解析

除了变换函数外,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

Array.filter将数组中不满足条件的元素过滤,我们可以用for循环加上Array.push来实现。

for-loop

下面这段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];
Array.filter

我们将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/201...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84571.html

相关文章

  • 【译】高阶函数:利用FilterMapReduce来编写更易维护代码

    摘要:原文作者译者高阶函数可以帮助你增强你的,让你的代码更具有声明性。知道什么时候和怎样使用高阶函数是至关重要的。太棒了我们通过使用高阶函数减少了许多额外的代码。 原文:Higher Order Functions: Using Filter, Map and Reduce for More Maintainable Code作者:Guido Schmitz译者:JeLewine 高阶函数可...

    jone5679 评论0 收藏0
  • 翻译连载 | JavaScript轻量级函数式编程-第 8 章:列表操作 |《你不知道JS》姊妹篇

    摘要:通过对一系列任务建模来理解一些非常重要的函数式编程在列表操作中的价值一些些看起来不像列表的语句作为列表操作,而不是单独执行。映射我们将采用最基础和最简单的操作来开启函数式编程列表操作的探索。函子是采用运算函数有效用操作的值。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着...

    sPeng 评论0 收藏0
  • 我是怎样用函数JavaScript计算数组平均值

    摘要:简单模式记录多个累加值在之前的版本中,我们创建了很多中间变量,。接下来,我们给自己设一个挑战,使用链式操作,将所有的函数调用组合起来,不再使用中间变量。你甚至可以继续简化上述代码,移除不必要的中间变量,让最终的计算代码只有一行。 译者按: 有时候一个算法的直观、简洁、高效是需要作出取舍的。 原文: FUNCTIONAL JAVASCRIPT: FIVE WAYS TO CALCULA...

    renweihub 评论0 收藏0
  • JavaScript 异步数组

    摘要:第二种则一定会执行所有的异步函数,即便你需要使用的是这些高阶函数。并发实现的异步数组然后修改,使用即可上面的其他内容终结整个链式操作并返回结果这里使用是为了兼容的调用方式调用方式不变。 JavaScript 异步数组 吾辈的博客原文: https://blog.rxliuli.com/p/5e... 场景 吾辈是一只在飞向太阳的萤火虫 JavaScript 中的数组是一个相当泛用性的...

    moven_j 评论0 收藏0
  • 一文带你了解什么是JavaScript 函数式编程?

    摘要:前言函数式编程在前端已经成为了一个非常热门的话题。整个过程就是体现了函数式编程的核心思想通过函数对数据进行转换。高阶函数函数式编程倾向于复用一组通用的函数功能来处理数据,它通过使用高阶函数来实现。 前言 函数式编程在前端已经成为了一个非常热门的话题。在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想。 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScrip...

    acrazing 评论0 收藏0

发表评论

0条评论

bawn

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<