乐趣区

JavaScript原生函数Arrayprototypereduce用法实战本质详解

reduce 函数是 JavaScript 的数组函数中,功能比较强大的函数。但是大部分博文对 reduce 函数的解释都是比较和基础。

reduce 的基础用法

我们先来看看 reduce 的基础用法,由于 reduce 的基础用法,在 MDN 里有比较详尽的解释,所以建议各位直接去看 MDN

JavaScript | MDN | Array.prototype.reduce()
里面有几个比较典型的例子

例 1. 数组去重:

var myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
var myOrderedArray = myArray.reduce(function (accumulator, currentValue) {if (accumulator.indexOf(currentValue) === -1) {accumulator.push(currentValue);
  }
  return accumulator
}, [])

console.log(myOrderedArray);

实际上,如果不用 Set,咋一看可以用 filter 实现,但是由于 filter 拿不到迭代的结果 array,所以用 filter 或者 forEach、map 实现都需要借助外部定义的数组,比如

var myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
var resultArray = [];
myArray.forEach(item => {if(resultArray.indexOf(item) === -1){resultArray.push(item);
    }
})

例 2. 迭代使用 Promise

function runPromiseInSequence(arr, input) {
  return arr.reduce((promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input)
  );
}

// promise function 1
function p1(a) {return new Promise((resolve, reject) => {resolve(a * 5);
  });
}

// promise function 2
function p2(a) {return new Promise((resolve, reject) => {resolve(a * 2);
  });
}

// function 3  - will be wrapped in a resolved promise by .then()
function f3(a) {return a * 3;}

// promise function 4
function p4(a) {return new Promise((resolve, reject) => {resolve(a * 4);
  });
}

const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
  .then(console.log);   // 1200

reduce 的特点就是在迭代过程中,可以使用之前的迭代结果

所以我们得出第一个结论:

迭代过程中,需要使用到迭代结果的,适合使用 reduce
反之,如果迭代过程中,不需要使用迭代结果,那么 Array.prototype 上的其他函数,完全可以胜任任何逻辑。

例 3:把 originArray 数组变成一个一维数组

let originArray = [1,[2,3],[4,[5,6,[7,8],9],10,[11,12,[13,14],15],16],17];
function smoothArray(array){return array.reduce((resultArray, currentValue) => {
        let concatArray;
        if(Array.isArray(currentValue)){concatArray = smoothArray(currentValue);
        }else{concatArray = [currentValue];
        }
        return resultArray.concat(concatArray);
    }, [])
}
smoothArray(originArray);
// 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]

本例中,我们通过递归的方式,将 N 维数组 originArray 降维变成了一维数组。
显而易见地,如果要把 originArray 变成一个 0 维数组(0 维数组就是一个值),通过这个方法也是可行的。变成 0 维数组(一个值)的方式,可以是累加、累乘、累减等方法。在这里,用什么方法是不重要的,重要的是 reduce 干了什么、什么是 reduce 的本质。

什么是 reduce 的本质?

抽象地,reduce 的调用者一定是一个数组,这个数组至少是一个一维数组。得到的结果是一个值,这个值可能是数组,可能是对象,可能是 JavaScript 基础类型中的一个值。
reduce 做的事情,是:
数组 => 一个值
我们和 Array.prototype 上的其他函数做个比较,可以发现,map、forEach、filter 一定返回一个数组;includes、find、findIndex 必然返回一个值;reduce 做的,是将一个高维度的东西,“压缩”成 一个值 的过程。
读过《三体》的朋友,一定对《三体 III》中描述的四维文明、以及太阳系二维化的情节有印象。降维的过程,几乎必然伴随着信息的丢失。reduce 也不例外。当你将一个数组通过 reduce 变成一个值的时候,或多或少,必然丢失了一些信息。

最后,总结一下 reduce 的本质就是:

降维

退出移动版