forEach

语法:

arr.forEach(callback(currentValue [, index [, array]])[, thisArg]);

注意:

  1. forEach调用时不会改变原数组,也就是调用它的数组
  2. forEach() 遍历的范围在第一次调用 callback 前就会确定。调用forEach后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback的值是 forEach()遍历到他们那一刻的值。已删除的项不会被遍历到。
  3. thisArg有值,当callback被调用时,this都会指向thisArg参数,如果被省略,或者thisArg的值为null或者undefined,this指向全局对象。

说明:1,2

var arr = [0,1,2];
arr.forEach(item => {
    arr[0]++; //验证arr值的修改是否影响item,并且验证arr长度的增加会不会影响运行的次数
    item++;   //验证修改item是否会影响arr
    console.log(item);    //输出结果为1,2,3
    arr.push(3);
});
console.log(arr);  //输出结果[3,1,2,3,3,3]

即:arr长度最后影响不了执行的次数(如果能影响arr[0]应该为6而不是3)说明在调用callback之前已经调用了它的数组的长度。item的更改影响不到原数组arr,即它的数组的值已被浅拷贝或深拷贝

验证:里面是深拷贝还是浅拷贝

var brr = [0,1,2,[3,4]];
brr.forEach(item=>{
    brr[3][0] = 1;
    console.log(item);  //0 1 2 [1,4]
});

即:浅拷贝

验证:回调函数的第三个参数为它的数组

var brr = [0,1,2];
brr.forEach((item, index, tempArr)=>{
    tempArr[2] = 5;
    console.log(item);  //0 1 5
});

手写forEach

Array.prototype.myForEach = function(callback, thisArg) {
    //1. 规定不许谁调用方法
    if(this == null) {
        throw new TypeError("this is null or not defined");
    }
    //2. 回调函数一定是函数
    if(typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function')
    }
    //3. 获取arr数组的引用,转换成对象
    var whoArr = Object(this);
    //4. 固定执行次数,一定为正数,无符号右移>>>
    var len = whoArr.length >>> 0;
    //计时器
    var k = 0;
    while(k < len) {
        //5. 查找whoArr属性
        if(k in whoArr) {
            callback.call(thisArg, whoArr[k], k, whoArr);
        }
        k++;
    }
}
//-----------------------测试
let arr = [0,1,2];
arr.myForEach(item=>{
    console.log(item);   //1,2,3
});

扩展(for与forEach的区别)

由手写forEach的可知,forEach是不能被break打断的,如果在里面添加break

let arr = [0,1,2];
arr.myForEach(item=>{
    break;
    console.log(item);   //1,2,3
});

forEach里面使用return没有返回值,手写里面只是调用回调函数。在for里面使用return会直接报错

map

Array.prototype.map()

map()方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

语法:

var new_array = arr.map(function callback(currentValue[, index[, array]]) {

}[, thisArg])

callback三个参数:1.当前值。2.当前索引(可选)3.当前数组(可选)

thisArg:callback的this指向

注意:

  1. map方法会给原数组的每个元素按顺序调用一次,每次执行后的返回值(包括undefined)组成一个新数组。callback函数只会在有值的索引上被调用,那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
  2. map不会修改原数组的本身,但可以在回调函数执行时改变原数组
  3. map处理数组元素的次数在回调函数第一次调用时候已经决定。存在的数组元素改变了,传给callback的值是map访问该元素时的值。在map函数调用后但在访问该元素前,该元素被删除的话,则无法被访问到。

实现

//1. thisArg没被传值即它的默认值为undefined
Array.prototype.myMap = function(callback, thisArg = undefined) {
    //2. 判断传入的第一个参数是否为函数
    if(Object.prototype.toString.call(callback) != "[object Function]") {
        throw new TypeError(callback + "is not a function");
    }
    //3. 处理数组类型异常
    if(this === undefined || this === null) {
        throw new TypeError("Cannot read property 'myMap' of null or undefined");
    }
    //4. 要先将this先转换成object,由草案可知
    const o = Object(this);
    //5. 获取长度,即在回调函数调用之前固定运行次数,次数为正数,使用无符号左移
    let len = o.length >>> 0;
    //6. 创建需要返回的新数组
    const arr = new Array(len);
    //7. 遍历开始运行回调函数
    for(let i=0; i<len; i++) {
        if(i in o) {
            let val = o[i];
            //8. 调用回调函数,传入参数,获取新的值
            let res = callback.call(thisArg, val, i, o);
            //9. 填入新数组
            arr[i] = res;
        }
    }
    //10. 返回新数组
    return arr;
}
//------------------------测试
const arr = [0,12];
const arr1 = arr.myMap(item=>item+1);
console.log(arr1);

reduce

Array.prototype.reduce()

reduce()该方法对数组中的每个元素执行一个由您提供的函数(升序执行),将其结果汇总为单个返回值。

语法:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

callback包含四个参数:accumulator为上一次回调返回的累积值,currentValue数组当前元素,index当前索引,array为调用reduce的数组

initialValue:作为第一次调用回调函数的累积值。如果没有则将使用数组第一个元素。没有initialValue在没有初始值的空数组上调用 reduce 将报错。

注意:

  1. initialValue有值,那么第一次回调执行时,accumulator取值为initialValue,currentValue取值为数组第一个值。initialValue没有提供,那么第一次回调执行时,accumulator取值为数组第一个值。,currentValue取值为数组第二个值。
  2. 调用reduce的数组为空,报错。数组只有一个元素且没有提供initialValue,或者提供initialValue但是空数组[],那么此唯一值将被返回并且callback不会被执行。
let count = 0;
console.log([, 1, 2].reduce((prev, cur) => {
    count++;
    return prev + cur;
}));
console.log(count);

由此得知:非空数组调用reduce,在没有传入initialValue值时,那么累积值accumulator就会在调用数组找到第一个非空empty值,cur就是该数组下一个值(没有下一个值,就直接将accumulator返回),进行第一次的回调函数。

实现:

Array.prototype.myReduce = function (callback, initialValue) {
    //1. 判断callback是否为函数
    if (Object.prototype.toString.call(callback) != "[object Function]") {
        throw new TypeError(callback + "is not a function");
    }
    //2. 处理调用数组类型异常
    if (this === undefined || this === null) {
        throw new TypeError("Cannot read property 'myReduce' of null or undefined");
    }
    //3. 转换成对象
    let o = Object(this);
    //4. 确定运行的次数必然为正数>>>位运算符
    let len = o.length >>> 0;
    //5. 累积值为initialValue
    let accumulator = initialValue;
    //6. i用来找到值的索引
    let i=0;
    //7. 先判断是否传入初始值
    if(accumulator == undefined) {
        //8. 没有传入初始值,就要去找到调用数组的第一个非空的元素
        while(i<len) {
            //9. 找出非空值为累积值
            if(i in o) {
                accumulator = o[i];
                //11. 当前i为第一次累积值accumulator,i++之后即去找currentValue值
                i++;
                break;
            }
            //10. 遍历的索引相加,都是空的
            i++;
        }
        //12. 遍历完过后,索引大于长度,那么数组里面全为空,直接报错
        if(i > len) {
            throw new TypeError("Each element of the array is empty");
        }
    }
    //13. 开始找到currentValue值
    for( ; i<len; i++) {
        //14. 也要不为空
        if(i in o) {
            //15. 获取每次回调函数运行后的累积值
            accumulator = callback.call(undefined, accumulator, o[i], i, o)
        }
    }
    //16. 返回结果累积值
    return accumulator;
}
console.log([, 1, 2, , 3].reduce((prev, cur) => {
    return prev + cur;
}));
console.log([, 1, 2, , 3].myReduce((prev, cur) => {
    return prev + cur;
}));

filter

创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

语法:

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

注意:

  1. callback只会在已经赋值的索引上调用,对于那些已经被删除或者从未赋值的索引不会被调用
  2. filter不会改变原数组,它返回过滤后的新数组
  3. filter的遍历范围在第一次调用callback之前已经被确定。

实现:

Array.prototype.myFilter = function (callback, thisArg) {
	//1. 判断callback是否为函数
	if(Object.prototype.toString.call(callback) !== "[object Function]") {
		throw new TypeError(callback + "is not a function");
	}
	//2. 判断this是否合法
	if(this === undefined || this === null) {
		throw new TypeError("Cannot read property 'myFilter' of null or undefined");
	}
	//3. 转换成对象
	let O = Object(this);
	//4. 范围已被确定且为正数
	let len = O.length >>> 0;
	//5. 创建新数组
	let res = [];
	//6. 遍历元素
	for(let i=0; i<len; i++) {
		//7. 跳过empty空
		if(i in O) {
			//8. 判断回调函数返回值
			if(callback.call(thisArg, O[i], i, O)) {
				res.push(O[i]);
			}
		}
	}
	//9. 返回过滤结果
	return res;
}
//------------------------测试用例
console.log([1,2,3].filter(item=> item > 1));
console.log([1,2,3].myFilter(item => item > 1));