首先,我们来了解一下什么是高阶函数,JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。那么js里面常见的高阶函数又有哪些呢?常见的比如Array.prototype.map() - JavaScript | MDN (mozilla.org)、Array.prototype.filter() - JavaScript | MDN (mozilla.org)和Array.prototype.reduce() - JavaScript | MDN (mozilla.org)等,建议点击链接认真查看有关内容。
map
map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值
let arr = [1,2,3,4];
let res = arr.map((item) => {
return item * item;
})
console.log(res);
输出结果如下:
接下来,我们来实现map函数
function selfMap(fn, context) {
let arr = Array.prototype.slice.call(this); //arr为测试数组
let newArr = new Array();
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
} //判断是否为稀疏数组
newArr[i] = fn.call(context, arr[i], i, this);
}
return newArr;
}
Array.prototype.selfMap = selfMap; // 挂载到Array的原型链上
let arr = [1, 2, 3, 4];
let res2 = arr.selfMap((item) => {
return item * item;
})
console.log(res2);
测试结果和map的结果一致
filter
filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
let arr = [1, 2, 3, 4];
let res = arr.filter((item) => {
return item > 2;
})
console.log(res);
输出结果如下:
接下来,我们来实现filter函数
let selfFilter = function (fn, context) {
let arr = Array.prototype.slice.call(this);
let finalArr = [];
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
} //判断是否为稀疏数组
if (fn.call(context, arr[i], i, this)) { // 判断是否符合条件
finalArr.push(arr[i]);
}
}
return finalArr;
}
Array.prototype.selfFilter = selfFilter;
let arr = [1, 2, 3, 4];
let res2 = arr.selfFilter((item) => {
return item > 2;
})
console.log(res2);
测试结果和filter的结果一致
reduce
reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被作为初始值 initialValue,迭代器将从第二个元素开始执行(索引为 1 而不是 0)。
let arr = [1, 2, 3, 4];
let res = arr.reduce((pre,cur) => {
return pre + cur;
},0)
console.log(res);
// 在上面的例子中,reduce实现的功能是求和,pre参数代表前一次计算的结果,cur代表当前值,0代表初始值,即在没有计算之前,pre的值
function selfReduce(fn, initValue) {
let arr = Array.prototype.slice.call(this);
let res;
let startIndex;
if (initValue == undefined) { //找到第一个非空单元的元素和下表
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
}
startIndex = i;
res = arr[i];
break;
}
}
else {
res = initValue;
}
//console.log(startIndex, res);
for (let i = ++startIndex || 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
}
res = fn.call(null, res, arr[i], i, this);
}
return res;
}
Array.prototype.selfReduce = selfReduce;
let arr = [1, 2, 3, 4];
let res2 = arr.selfReduce((pre,cur) => {
return pre + cur;
},0)
console.log(res2);
测试结果和reduce一致
利用reduce实现map和filter
如果已经理解前面三个高阶函数的用法,就可以自主尝试一下利用reduce实现map和filter,这里也简单实现一下
利用reduce实现map
let arr = [1, 2, 3, 4];
let res = arr.map((item) => {
return item * item;
})
console.log(res);
function selfMap2(fn, context) {
let arr = Array.prototype.slice.call(this);
return arr.reduce((pre, cur, index) => {
return [...pre, fn.call(context, cur, index, this)]
}, [])
}
Array.prototype.selfMap2 = selfMap2;
let res2 = arr.selfMap2((item) => {
return item * item;
})
console.log(res2);
利用reduce实现filter
let arr = [1, 2, 3, 4];
let res = arr.filter((item) => {
return item > 2;
})
console.log(res);
function selfFilter2(fn, context) {
let arr = Array.prototype.slice.call(this);
return arr.reduce((pre, cur, index) => {
return fn.call(context, arr[index], index, this) ? [...pre, cur] : [...pre];
}, [])
}
Array.prototype.selfFilter2 = selfFilter2;
let res2 = arr.selfFilter2((item) => {
return item > 2;
})
console.log(res2);
flat
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。示例参考自Array.prototype.flat() - JavaScript | MDN (mozilla.org)
let arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
let arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
let arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity,可展开任意深度的嵌套数组
let arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//flat方法会移除数组中的空项
let arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
方法1
function selfFlat2(depth = 1) { //提取嵌套数组的结构深度默认值为1
let arr = Array.prototype.slice.call(this);
let newArr = [];
if (depth == 0) {
return arr;
}
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
newArr.push(...selfFlat2.call(arr[i], depth - 1));
}
else {
if (arr[i]){ // 如果数组某个值为空,不填入新数组
newArr.push(arr[i]);
}
}
}
return newArr;
}
// 这里只展示测试的部分示例,感兴趣的可自行测试其他例子
Array.prototype.selfFlat2 = selfFlat2;
console.log([1, 2, [3, 4], ,[5, [6, 7, [8, ,9]]]].selfFlat2(2));
方法2
function selfFlat(depth = 1) {
let arr = Array.prototype.slice.call(this);
if (depth == 0) {
return arr;
}
return arr.reduce((pre, cur, index) => {
if (Array.isArray(cur)) {
return [...pre, ...selfFlat.call(cur, depth - 1)]
}
else {
if(cur){
return [...pre, cur];
}
}
}, [])
}
// 这里只展示测试的部分示例,感兴趣的可自行测试其他例子
Array.prototype.selfFlat = selfFlat;
console.log([1, 2, [3, 4],, [5, [6, 7, [8,, 9]]]].selfFlat());
方法3
function flattern(arr) {
return arr.join().split(',').map((item) => {
if (item != undefined){
return parseInt(item);
}
}).filter((item) => {
if (!isNaN(item)){
return item;
}
});
//如果不加上filter函数,返回的结果是[1, 2, 3, NaN, 4, 6, NaN, 7, 5],其中会包含NaN
}
console.log(flattern([1, 2, 3,, [4,[6,,7], 5]]));
join() 方法用于把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。如果省略该参数,则使用逗号作为分隔符。(这里还有一个小插曲,当时我贝壳二面的时候,面试问我数组扁平化的方法,当时我说到了这个方法,写出来之后,面试官问我有没有问题,我很肯定,一点问题都没有,我之前有测试过。但是面试官一直坚持说split不传入参数跑不出来,我当时面试的时候,就紧张起来了,想质疑他来着,但还是缺乏了一点勇气,就说自己下去之后再试试。查阅了资料之后才发现默认参数就是",",有点后悔没有继续“刚”下去,即时最后还是给了我offer)
split() 方法用于把一个字符串分割成字符串数组。
方法4
//[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]
// es6的扩展运算符可以将二维数组变成一位数组
let arr = [1, 2, 3, , [4, [6, , 7, [8,9,,[10]]], 5]];
function flattern(arr) {
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr.filter((item) => {
return item != undefined;
})
}
console.log(flattern(arr));