循环

实现一下数组方法 forEach,map,reduce,filter

先写自己实现的想法,可能有疏漏和错误,发现了再更改。
1.forEach

首先了解一下forEach

1
array.forEach(callback[, thisObject]);

写个例子观察一下

1
2
3
4
5
6
7
8
9
10
11
12
13
var arr = [1,2,3];
var obj = {
arr1:[]
}
arr.forEach(function(item,index,array){
console.log(item,index,array);
this.arr1.push(item);
},obj);
console.log(obj);
//1 0 [ 1, 2, 3 ]
//2 1 [ 1, 2, 3 ]
//3 2 [ 1, 2, 3 ]
//{ arr1: [ 1, 2, 3 ] }
特点:

1)forEach方法接受2个参数,第一个callback,第二个上下文环境

2)callback如果传入是不是方法,提示XX is not a function

3)callback有三个参数,分别是项,下标,和原数组

4)在callback中第三个参数array,如果修改其中的值,会改变原数组

实现
1
2
3
4
5
6
7
Array.prototype.forEach = function (cb, ctx) {
if (typeof cb !== 'function') throw new Error(cb + ' is not a function');
var len = this.length;
for (var i = 0; i < len; i++) {
cb.call(ctx, this[i], i, this);
}
}
2.map

map其实和forEach有点类似

1
array.map(callback,[ thisObject]);

写个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [1, 2, 3];
var obj = {
arr1: []
}
var newArr = arr.map(function (item, index, array) {
array[index] = 0;
this.arr1.push(item);
return item * 2;
}, obj);
console.log(arr);
console.log(obj);
console.log(newArr);
//[ 0, 0, 0 ]
//{ arr1: [ 1, 2, 3 ] }
//[ 2, 4, 6 ]
特点:

1)map方法接受2个参数,第一个callback,第二个上下文环境

2)callback如果传入是不是方法,提示`XX is not a function

3)callback有三个参数,分别是项,下标,和原数组

4)在callback中第三个参数array,如果修改其中的值,会改变原数组

5)在callback里有return的话,return的值就是新数组的每一项。没有return的话返回一个全是undefined的数组(也就是map会返回一个新数组,所以支持链式调用)

实现
1
2
3
4
5
6
7
8
9
10
Array.prototype.map = function (cb, ctx) {
if (typeof cb !== 'function') throw new Error(cb + ' is not a function');
var newArray = [];
var len = this.length;
for (var i = 0; i < len; i++) {
var result = cb.call(ctx, this[i], i, this);
newArray[i] = result;
}
return newArray;
}
3.reduce

语法:

1
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

写个例子

1
2
3
4
5
6
7
8
var arr = [1, 2, 3];
arr.reduce(function(total,currentValue,index,arr){
console.log(total);
console.log(currentValue);
console.log(index);
console.log(arr);
return total + currentValue;
},1)
特点:

1)reduce方法接受2个参数,一个是callback,一个是初始值initalValue

2)callback有4个参数,分别是总的值,当前的值,下标,和原数组

3)callback如果传入是不是方法,提示`XX is not a function

4)callback函数里返回值会被total保存

5)reduce函数调用会返回total的值

实现:
1
2
3
4
5
6
7
8
9
Array.prototype.reduce = function (cb, initialValue) {
if (typeof cb !== 'function') throw new Error(cb + ' is not a function');
var total = initialValue || this[0];
var len = this.length;
for (var i = 0; i < len; i++) {
total = cb.call(null,total,this[i],i,this);
}
return total;
}
4.filter

语法:

1
array.filter(callback,[ thisObject]);

1)filter方法接受2个参数,一个是callback,第二个是上下文环境

2)callback有3个参数,分别是当前值,下标和原数组

3)会返回一个新数组

4)在callback中第三个参数array,如果修改其中的值,会改变原数组

5)如果callback里有返回值,并且是true,会将当前的值放进数组中

写个例子

1
2
3
4
5
6
7
8
9
10
11
12
var arr = [1, 2, 3];
var obj = {
arr1: []
}
var newArr = arr.filter(function (item, index, array) {
array[index] = 0;
this.arr1.push(item);
return item > 2;
}, obj);
console.log(arr);//[ 0, 0, 0 ]
console.log(obj);//{ arr1: [ 1, 2, 3 ] }
console.log(newArr);//[ 3 ]
实现:
1
2
3
4
5
6
7
8
9
10
11
12
Array.prototype.filter = function (cb, ctx) {
if (typeof cb !== 'function') throw new Error(cb + ' is not a function');
var newArray = [];
var len = this.length;
for (var i = 0; i < len; i++) {
var item = this[i];
if (cb.call(ctx, item, i, this)) {
newArray.push(item);
}
}
return newArray;
}

看一下MDN上实现的

先说一下Object当以非构造函数形式被调用时,Object 等同于 new Object()。如果参数是符合类型,数据被改变的话,会改变原先的数据

1
2
3
4
var arr = [1,2,3];
var arr1 = new Object(arr);
arr1[0] = 0;
console.log(arr);//[0,2,3]
1. forEach
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.forEach = function (callback, thisArg) {
var T, k;
if (this == null) {//this指向Array
throw new TypeError(' this is null or not defined');
}
var O = Object(this);//当以非构造函数形式被调用时,Object 等同于 new Object()。
var len = O.length >>> 0;//得到数组的length(>>> 0)据说会把不是number类型的数据转成number类型。
if (typeof callback !== "function") {//callback类型不是function,就扔出类型错误
throw new TypeError(callback + ' is not a function');
}
if (arguments.length > 1) {//如果参数的length大于0,说明传入了thisArg
T = thisArg;
}
k = 0;
while (k < len) {//当k不小于数组的长度的时候结束循环
var kValue;
if (k in O) {//k会是数组的下标
kValue = O[k];
callback.call(T, kValue, k, O);//T是上下文,kValue是当前下标对应的值,k是下标,0是原数组
}
k++;
}
//返回undefined
};
2.map
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
Array.prototype.map = function (callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);//当以非构造函数形式被调用时,Object 等同于 new Object()。
var len = O.length >>> 0;

if (Object.prototype.toString.call(callback) != "[object Function]") {//typeof和instanceof有缺陷,无法区分对象,数组(null比较特别)
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
T = thisArg;
}
A = new Array(len);
k = 0;
while (k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
A[k] = mappedValue;
}
k++;
}

// 返回新数组A
return A;
};
3.reduce
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
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback , initialValue) {
if (this === null) {
throw new TypeError( 'Array.prototype.reduce ' +
'called on null or undefined' );
}
if (typeof callback !== 'function') {
throw new TypeError( callback +
' is not a function');
}
//上面两步就是判断数组是否存在,callback是否是方法
var o = Object(this);//当以非构造函数形式被调用时,Object 等同于 new Object()。
var len = o.length >>> 0; //取得长度
var k = 0;
var value;//声明初始值
if (arguments.length >= 2) {//如果第二个参数存在,就设为初始值
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
if (k >= len) {
throw new TypeError( 'Reduce of empty array ' +
'with no initial value' );
}
value = o[k++];
}
while (k < len) {
if (k in o) {
value = callback(value, o[k], k, o);
}
k++;
}
return value;
}
});
4.filter
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
Array.prototype.filter = function(fun , thisArg)
{
"use strict";

if (this === void 0 || this === null)//我的理解是undefined等于void 0
throw new TypeError();

var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();

var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++)
{
if (i in t)
{
var val = t[i];
if (fun.call(thisArg, val, i, t))
res.push(val);
}
}

return res;
};

总结

forEach和map

共同点:修改callback中的第三个array参数,会改变原数组。

不同点:map支持链式调用,并且会返回一个新数组。而forEach返回undefined。