filter()/map()/reduce()都是es6新增的对于数组的方法,本篇文章来记录手写这3个函数以及forEach()的过程
filter()方法
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
filter翻译过来就是过滤器的意思[].filter(function(currentValue,index,arr), thisValue)
,它接受2个参数:
function(currentValue,index,arr)
: 必须传入的函数,每个数组元素都会执行这个函数,currentValue是当前元素,index是序号,arr是对应的整个数组
thisValue
: 对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
具体用法:
// 返回大于等于18的值
let arr = [32, 33, 16, 40].filter((num) => {return num >= 18})
console.log(arr)
手写实现
Array.prototype.myFilter = function(callback, thisArg) {
// 确认调用者必须是个数组
if (Object.prototype.toString.call(this) !== '[object Array]') {
throw new TypeError('this must be a array');
}
// 确认传入的第一个参数必须是函数且存在
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
// 返回结果的数组
const res = [];
// 让O成为回调函数的对象传递(强制转换对象)
const O = Object(this);
console.log(O)
// >>>0 保证len为number,且为正整数
// 无符号位移计算符
const len = O.length >>> 0;
// 对整个数组进行遍历
for (let i = 0; i < len; i++) {
// 遍历回调函数调用传参
// call是传入(新this指向,参数)
// thisArg新设置的this,这里无设置就是undefined
// O[i] 是原数组的当前元素
// i是当前index
// O是原数组
if (callback.call(thisArg, O[i], i, O)) {
res.push(O[i]);
}
}
// 返回结果
return res;
}
console.log([30,20,16,10].myFilter((num) => { return num >= 12}));
根据上面的数组[30,20,16,10]
,O就是原数组,每次都判断每个数是否大于等于12,如果大于,就通过if (callback.call(thisArg, O[i], i, O))
的判断,并且存入res数组,最后返回的是经过过滤的数组。
map()
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 会对所有的数据进行处理,[].filter(function(currentValue,index,arr), thisValue)
接受的参数和filter()
一样。
function(currentValue,index,arr)
: 必须传入的函数,每个数组元素都会执行这个函数,currentValue是当前元素,index是序号,arr是对应的整个数组
thisValue
: 对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
// 对每个值进行*2处理
let arr = [32, 33, 16, 40].map((num) => {return num * 2})
console.log(arr)
手写实现
Array.prototype.myMap = function(callback, thisArg) {
if (this == undefined) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const res = [];
const O = Object(this);
const len = O.length >>> 0;
for (let i = 0; i < len; i++) {
// 调用回调函数并传入新数组
res[i] = callback.call(thisArg, O[i], i, O);
}
return res;
}
可以看见map()
和filter()
的实现方式基本一致,无非是filter()
多了一个if (callback.call(thisArg, O[i], i, O))
的判断,从而实现过滤的效果
forEach()
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
使用方法[].forEach(function(currentValue, index, arr), thisValue)
function(currentValue,index,arr)
: 必须传入的函数,每个数组元素都会执行这个函数,currentValue是当前元素,index是序号,arr是对应的整个数组
thisValue
: 对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
手写实现
Array.prototype.myForEach = function(callback, thisArg) {
// 判断是否是数组调用,并且传入的是回调函数
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let k = 0;
// 循环所有数据
for(let i = 0; i < len; i++) {
callback.call(thisArg, O[k], k, O);
}
}
forEach()
和map()
就更像了,但是查看源码forEach()
是直接在原数组上操作返回undefined,而map()
是返回个新数组。
同时还有以下区别:
-
forEach()
的速度更快 -
map()
因为返回数组所以可以链式操作,forEach()
不能 -
map()
里可以用return,而foreach里用return不起作用,forEach()
也不能使用break/continue
reduce()
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
使用方法array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
function(total, currentValue, currentIndex, arr)
:必要参数,用于执行每个数组元素的函数 total
是计算结束后的返回值,currentValue
当前元素,currentIndex
当前元素的索引,arr
元素所属的数组对象
initialValue
:传递的初始值
console.log([1,2,3].reduce((t,num) => {return t + num}))
console.log([1,2,3].reduce((t,num) => {return t + num},5))
手写实现
Array.prototype.myReduce = function(callback, initialValue) {
// 判断调用的是否是数组,以及传入的callback是否是函数
if (this == undefined) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
// 空数组也是不允许的
if (this.length == 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
// 让O成为回调函数的对象传递(强制转换对象)
const O = Object(this);
// >>>0 保证len为number,且为正整数
const len = this.length >>> 0;
// 保存初始值,初始值不传的时候为undefined
let accumulator = initialValue;
// 标志位
let k = 0;
// 如果第二个参数为undefined的情况,则数组的第一个有效值作为累加器的初始值
if (accumulator === undefined) {
// 这里是k++,就是赋值完成之后k再加1
accumulator = O[k++];
}
// 此时如果有初始值,k是0,如果无初始值k是1
for(k;k{return t+n}));
console.log([2,4,6].myReduce((t,n)=>{return t+n},10));
要确认不能是空数组、必须是数组调用、callback必须是函数这3个条件,之后就是循环执行了,理解起来还是很容易的,多看几遍代码就能了解