ES6 Array系列(四) Array常用的方法和实现reduce、map、filter、forEach

挫其銳,解其紛,和其光,同其塵。

ES6 Array 系列(一) 一些常用 array 的扩展方法
ES6 Array 系列(二) 一些常用 array 的扩展方法(二)
ES6 Array 系列(三) Array 中的 forEach 方法可以用 break、continue 跳出循环?
ES6 Array 系列(四) Array 常用的方法和实现 reduce、map、filter、forEach

简介


Array.prototype上有很多方法,可以很方便的实现各种循环、过滤对数组做很多的处理,这里主要记录自己怎么实现几个方法mapforEachfilterreduce,怎么使用就不多做讲解了因为在mdn 中 Array或者别人的文章中有很多的讲解了。

实现 reduce


首先要了解reduce它有两个参数,第一个参数是一个回调方法callback,第二个参数是一个初始值initialValue
大致实现步骤如下:

  • 判断参数,判断调用方法本身是否为 Array
  • 声明要用的变量
  • 判断是否有初始值,如果没有则从本身数组中取,取到直接跳出循环
  • 循环调用 callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Array.prototype.selfReduce = function(callback, initalValue) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 声明要用到的变量
// 要循环的数组
let arr = Array.prototype.slice.call(this);
let _len = arr.length;
// 结果数组
let res = [];
// 开始的数组索引 默认为 0
let startIndex = 0;

// 判断是否为有初始化值 initalValue
if (initalValue === undefined) {
// 如果初始值为 undefined 循环从数组中找到有值,并且退出循环
// 过滤稀疏值
for (let i = 0; i < _len; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
} else {
startIndex = i;
res = arr[i];
break;
}
}
} else {
res = initalValue;
}

// 在上一步拿到初始值,循环调用传入的回调函数,并且过滤松散值
for (let i = startIndex++; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) {
continue;
}
res = callback.call(null, res, arr[i], i, this);
}
return res;
};

测试一下selfReducereduce方法是否表现一致,代码如下:

1
2
3
4
5
6
7
var aTest = [1, 2, 3, 4, 5];
arr.reduce((prev, next) => {
return prev + next;
}, 0); // 15
arr.selfReduce((prev, next) => {
return prev + next;
}, 0); // 15

其实也可以去看一下官方的mdn reduce polyfill

实现 map


map的使用这里不多做赘述,只记录它的两种实现方式一种通过for循环实现,另一种通过reduce实现。

for 循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Array.prototype.selfMap = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 声明要用到的变量
let arr = Array.prototype.slice.call(this);
let _len = arr.length;
let aMap = [];
// 循环调用
for (let i = 0; i < _len; i++) {
// 过滤稀疏值
if (!arr.hasOwnProperty(i)) {
continue;
}
aMap[i] = callback.call(context, arr[i], i, this);
}
return aMap;
};

测试实现selfMapmap是否一致,代码如下:

1
2
3
4
5
6
7
var aTest = [1, 2, 3, 4, 5];
aTest.map(item => {
return item * 2;
}); // [2, 4, 6, 8, 10]
aTest.selfMap(item => {
return item * 2;
}); // [2, 4, 6, 8, 10]

reduce 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Array.prototype.reduceMap = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 获取数组
let aMap = Array.prototype.slice.call(this);
// 使用reduce实现循环
return aMap.reduce((pre, cur, index) => {
// 拼接上次循环结果和当前结果
// 循环调用callback
return [...pre, callback.call(context, cur, index, this)];
}, []);
};

测试实现reduceMapmap是否一致,代码如下:

1
2
3
4
5
6
7
var aTest = [1, 2, 3, 4, 5];
aTest.map(item => {
return item * 2;
}); // [2, 4, 6, 8, 10]
aTest.reduceMap(item => {
return item * 2;
}); // [2, 4, 6, 8, 10]

filter 实现


filter 也用到很多次,这里也不多做赘述直接看两种实现方法:

for 循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Array.prototype.selfFilter = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 获取数组
let aArr = Array.prototype.slice.call(this);
let _len = aArr.length;
let aFArr = [];
// 循环调用callback
for (let i = 0; i < _len; i++) {
if (!aArr.hasOwnProperty(i)) {
continue;
}
callback.call(context, aArr[i], i, this) && aFArr.push(i);
}
return aFArr;
};

reduce 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Array.prototype.reduceFilter = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 获取数组
let aArr = Array.prototype.slice.call(this);

// 循环调用callback
aArr.reduce((pre, cur, index) => {
return callback.call(context, cur, index, this) ? [...pre, cur] : [...pre];
}, []);

return aArr;
};

测试代码

1
2
3
4
5
6
7
8
9
10
var aTest = [1, 2, 3, 4, 5, 6];
aTest.filter(item => {
return item > 2;
});
aTest.selfFilter(item => {
return item > 2;
});
aTest.reduceFilter(item => {
return item > 2;
});

实现 forEach


for 循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Array.prototype.selfForeach = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 获取数组
let arr = Array.prototype.slice.call(this);
let _len = arr.length;
for (let i = 0; i < _len; i++) {
callback.call(context, arr[i], i, arr);
}
return arr;
};

reduce 循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Array.prototype.reduceForeach = function(callback, context) {
// 不能是null调用方法
if (this === null) {
throw new TypeError(
"Array.prototype.reduce" + "called on null or undefined"
);
}
// 第一个参数必须要为function
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 获取数组
let arr = Array.prototype.slice.call(this);
arr.reduce((pre, cur, index) => {
return [...pre, callback.call(context, cur, index, this)];
}, []);
};

总结

基本上理解了怎么实现,无论是使用 for 来实现还是用 reduce 实现,基本上没有太大的差别。

参考

Array.prototype.reduce()> 一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧
JS Array.reduce 实现 Array.map 和 Array.filter