forEach
语法:
arr.forEach(callback(currentValue [, index [, array]])[, thisArg]);
注意:
- forEach调用时不会改变原数组,也就是调用它的数组
- forEach() 遍历的范围在第一次调用 callback 前就会确定。调用forEach后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback的值是 forEach()遍历到他们那一刻的值。已删除的项不会被遍历到。
- 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指向
注意:
- map方法会给原数组的每个元素按顺序调用一次,每次执行后的返回值(包括undefined)组成一个新数组。callback函数只会在有值的索引上被调用,那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
- map不会修改原数组的本身,但可以在回调函数执行时改变原数组
- 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,1,2];
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 将报错。
注意:
- initialValue有值,那么第一次回调执行时,accumulator取值为initialValue,currentValue取值为数组第一个值。initialValue没有提供,那么第一次回调执行时,accumulator取值为数组第一个值。,currentValue取值为数组第二个值。
- 调用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])
注意:
- callback只会在已经赋值的索引上调用,对于那些已经被删除或者从未赋值的索引不会被调用
- filter不会改变原数组,它返回过滤后的新数组
- 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));