一提到循环,我们都会条件反射的想到 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]
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 }
如果多个元素都满足 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
findIndex
和 find
十分相似,唯一不同的就是,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
filter
和 find
也十分相似,唯一不同的就是,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
some
和 find
也十分相似,唯一不同的就是,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
every
和 some
有些相似, some
只要其中一个元素满足条件,就会返回 true
,every
则就像它的字面意思,需要所有的元素都满足条件才会返回 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;
};