跳到主要内容

数组实用方法forEach-map-find-findIndex-filter-some-every-sort-reduce-flat

· 阅读需 10 分钟

一提到循环,我们都会条件反射的想到 for 循环,虽然 for 循环能解决代码中大部分的问题,但是一旦遇到多重循环,其代码就显得不是那么优雅简洁了。在特定的场合中,合理运用 forEach, map, find, findIndex, filter, some, every, sort, reduce 这几个数组方法,就能够让我们事半功倍。

通往成功的道路有很多,但是总有一条路是最捷径的

forEach

forEach 用于循环遍历数组中的每一项,只能遍历数组

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.forEach((item, index, origin) => {
console.log(item.age, index);
});
// 12 0
// 14 0
// 16 0
 

第三个参数 origin 是原数组

原理

Array.prototype.myForEach = function (callback) {
for (let index = 0; index < this.length; index++) {
// 每次循环时,执行一次 `myForEach` 的回调函数,依次传入当前循环的元素,下标,和原数组
callback(this[index], index, this);
}
};

map

map 用于在遍历的过程中,在回调函数中对每一项进行重建,并生成新数组

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.map((item, index, origin) => {
return {
...item,
age: item.age * 2
};
});
// [
// { name: '张三', age: 24 },
// { name: '李四', age: 26 },
// { name: '王五', age: 28 }
// ]
// 简写
arr.map(item => item.age * 2);
// [24, 26, 28]
&nbsp;

map会返回新的数组,不会修改原数组

原理

Array.prototype.myMap = function (callback) {
const arr = [];
for (let index = 0; index < this.length; index++) {
// 取出回调函数返回的每一项,重新生成新的数组
const item = callback(this[index], index, this);
arr[index] = item;
}

return arr;
};

find

find 用于在遍历中找出指定的一项(条件为回调函数中的 return 值为 true),如果没找到则返回 undefined

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.find((item, index, origin) => {
return item.age === 14;
});
// { name: '李四', age: 14 }
&nbsp;

如果多个元素都满足 find 条件,也只会返回按下标顺序第一个匹配的元素

原理

Array.prototype.myFind = function (callback) {
for (let index = 0; index < this.length; index++) {
// 如果回调函数返回为 true,则直接返回当前元素给 myFind 方法
const item = callback(this[index], index, this);
if (item) return this[index];
}
};

findIndex

findIndexfind 十分相似,唯一不同的就是,findIndex 返回的是当前匹配元素的下标 index

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.findIndex((item, index, origin) => {
return item.age === 14;
});
// 1

原理

Array.prototype.myFindIndex = function (callback) {
for (let index = 0; index < this.length; index++) {
// 如果回调函数返回为 true,则直接返回当前元素的下标给 myFind 方法
const item = callback(this[index], index, this);
if (item) return index;
}
};

filter

filterfind 也十分相似,唯一不同的就是,filter 返回所有满足 find 条件的集合(数组结构)

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.filter((item, index, origin) => {
return item.age >= 13;
});
// [
// { name: '李四', age: 14 },
// { name: '王五', age: 16 }
// ]

原理

Array.prototype.myFilter = function (callback) {
const arr = [];

for (let index = 0; index < this.length; index++) {
// 如果回调函数返回为 true,则直接返回当前元素的下标给 myFind 方法
const item = callback(this[index], index, this);
if (item) {
arr[index] = this[index];
}
}

return arr;
};

some

somefind 也十分相似,唯一不同的就是,some 返回的值为 Boolean 类型

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.some((item, index, origin) => {
return item.age === 14;
});
// true

原理

Array.prototype.mySome = function (callback) {
for (let index = 0; index < this.length; index++) {
// 如果有任意一个元素满足条件,则直接返回 true
const item = callback(this[index], index, this);
if (item) return true;
}
// 所有元素都没满足条件
return false;
};

every

everysome 有些相似, some 只要其中一个元素满足条件,就会返回 trueevery 则就像它的字面意思,需要所有的元素都满足条件才会返回 true

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.every((item, index, origin) => {
return item.age <= 15;
});
// false

arr.every((item, index, origin) => {
return item.age % 2 === 0;
});
// true

原理

Array.prototype.myEvery = function (callback) {
for (let index = 0; index < this.length; index++) {
// 如果其中有任意元素不满足条件,则直接返回fasle
const item = callback(this[index], index, this);
if (!item) return false;
}
// 所有元素都满足条件了
return true;
};

sort

sort 方法实现了数组的排序。其内部通过冒泡排序,每次将前一个元素和后一个元素依次作为回调函数中的参数。如果函数返回值小于 0,则是升序; 如果返回值大于 0,则是降序。

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.sort((a, b) => a.age - b.age);
// [
// { name: '张三', age: 12 },
// { name: '李四', age: 14 },
// { name: '王五', age: 16 }
// ];

arr.sort((a, b) => b.age - a.age);
// [
// { name: '王五', age: 16 },
// { name: '李四', age: 14 },
// { name: '张三', age: 12 }
// ];

原理

Array.prototype.mySort = function (callback) {
// 为了不修改原数组,用一个新的变量接收
const arr = this.slice();
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
// 通过一次二维循环,对arr[j]和arr[j + 1]两个相邻元素比较,大的元素交换到后面
if (callback(arr[j], arr[j + 1]) > 0) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
};

reduce

reduce 用于对数组中的每个值(从左到右)开始缩减,最终计算为一个值

const arr = [
{ name: '张三', age: 12 },
{ name: '李四', age: 14 },
{ name: '王五', age: 16 }
];
arr.reduce((total, next, index, origin) => {
return total + next.age;
}, 0);

如果数组可能为空,则必须提供初始值给 `reduce`,也就是第二个参数

如果不提供初始值,则默认是 0

原理

Array.prototype.myReduce = function (callback, initValue) {
for (let index = 0; index <= this.length - 1; i++) {
// 每次遍历时把上一次计算的结果(回调函数的返回值)放在一个参数返回
initValue = fn(initValue, this[index], index, this);
}
// 循环结束时,initValue则作为最终结果作为 reduce 函数的返回值
return initValue;
};

flat

flat 用于展开多维数组(多维数组扁平化)

// 定义一个一维数组
let arr1 = [1, 2, 3, 4, 5];
// 转换成二维数组
arr1 = arr1.map(x => [x, x * 2]);
// [[1, 2],
// 1: (2)[2, 4]
// 2: (2)[3, 6]
// 3: (2)[4, 8]
// 4: (2)[5, 10]

arr1.flat();
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // 默认只展开一层
// [1, 2, 3, 4, [5, 6]]
arr2.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity,可展开任意深度的嵌套数组
const 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

Array.prototype.flat = function () {
const arr = [];
this.forEach(item => {
if (Array.isArray(item)) {
arr = arr.concat(item.flat()); // 如果是数组的话继续循环
} else {
arr.push(item);
}
});
return arr;
};