zoukankan      html  css  js  c++  java
  • 如何实现JavaScript的Map和Filter函数?

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

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

    译者: Fundebug

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

    这篇文章面向那些已经熟练使用for循环,但对Array.mapArray.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改为mapmultiplyFunction改为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重命名为filtergreaterThan重命名为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:

    Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了7亿+错误事件,得到了Google、360、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

     

  • 相关阅读:
    布局管理
    菜单和工具栏
    Hello World,本章学习Qt的基本功能
    PyQt5中文手册
    qemu-kvm使用
    查看内核日志
    tmux快捷键
    vim插件YouCompleteMe安装
    git submodule删除多余模块
    vim ctags
  • 原文地址:https://www.cnblogs.com/fundebug/p/7239383.html
Copyright © 2011-2022 走看看