Array方法重写

277 阅读3分钟

Array.prototype.forEach

<script>
    const oLi = document.getElementsByTagName("li");
    const arr = [1,3,6,7,8,9];

    //	forEach(cb, thisArg)
    //	forEach接受两个参数
    //	cb -> 为数组中每个元素执行的函数, cb(currentVal, index, array)回调函数接受三个参数
    //                                    ->currentVal 数组中正在处理的当前元素 	
   //                                     ->index: 数组中正在处理的当前元素的索引(可选)
    //					  ->array: forEach正在操作的数组(可选)
    //	thisArg -> 可选参数, 当执行回调函数cb时,用作this的值

    Array.prototype.myForEach = function(fn) {
            const _data = this,		//得到待循环的数组
                  len = this.length,
                  arg2 = arguments[1] || window;    //arg2为执行回调时用作的this
            for(let i = 0; i < len; i++) {
                  fn.apply(arg2, [_data[i], i, _data]);//执行回调函数,并且把this改为用户传进来的arg2,以及传递给cb需要的参数(_data[i], i, _data)
            }
    }
    arr.myForEach(function(item, index) {
        console.log(this, item)
    }, oLi);
    </script>	

结果为:

image.png

Array.prototype.filter

重写filter
用法  const newArr = arr.filter(callback(element, index, array), thisArg)
                    callback: 用来测试数组的每个元素的函数,返回true表示该元素通过测试,false则表示不保留  
                            ->element: 数组当前正在处理的元素  
                            ->index: 正在处理元素的索引  
                            ->array: 调用了filter的数组本身  
                    thisArg: 执行callback时,用于this的值
//过滤掉偶数
const arr = [1,2,3,4,5,6,7,8,9];
Array.prototype.myFilter = function(cb) {
    const _data = this,
          len = _data.length,
          arg1 = arguments[1] || window,
          newArr = [];
    for(let i = 0; i < len; i++) {
        const item = JSON.parse(JSON.stringify(_data[i]))    //这里进行深拷贝,因为如果数组中每一项是数组或者对象的话,把对象数组push进一个数组中,如果_data[i]改变,那么filter之后的每一项也会跟着变
        cb.apply(arg1, [_data[i], i, _data) && newArr.push(_data[i]);
    }
    return newArr;
}
const ret = arr.myFilter((item) => item % 2 != 0);

//结果为: [1,3,5,7,9];

Array.prototype.map

	
//映射 map
// const newArr = arr.map(cb(currentVal, index, array), thisArg);
//			  cb-> 生成新数组的函数,使用三个参数
//			       currentVal-> 数组中正在处理的当前元素
//			       index -> 数组中正在处理的当前元素的索引
//			       array -> map方法调用的数组
//			  thisArg -> 执行cb的函数是用作的this
//返回值-> 一个由原数组每个元素执行回调函数的结果组成的新数组

Array.prototype.myMap = function(cb) {
    const _data = this,
           len = _data.length,
           arg1 = arguments[1] || window,
           newArr = [];
    for(let i = 0; i < len; i++) {
        newArr.push(cb.apply(arg1, [_data[i], i, _data]));
    }
    return newArr;
}
const arr = [1,3,5,7,9];
const newArr = arr.myMap((item) => item * 2);
//newArr: [2,6,10,14,18]

Array.prototype.every

首先说一下every的用法以及特点

every()是用来测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个bool值

注意: 若此方法收到一个空数组,在一切情况下都返回 true

const ret = arr.every(cb(element, index, array), thisArg);
                      cb-> 用来测试每个元素的函数,可以接受3个参数
                             element-> 用于测试的当前值
                             index-> 用于测试的当前值的索引
                             array-> 调用every的当前数组
                      thisArg-> 执行callback时用作的this
// 在写every的是实现时我们首先要了解一下every的特点
// 1. 当有一个不满足cb回调函数的条件时就停止遍历
// 2. 返回一个bool值

Array.prototype.myEvery = function(cb) {
    let _data = this,
          len = _data.length,
          arg1 = arguments[1],
          ret = true;
    for(let i = 0; i < len; i++) {
        if(!cb.apply(thisArg, [_data[i], i, _data])) {
            ret = false;
            break;
        }
    }
    return ret;
    
}
const data = [
                {
                        id: 1,
                        course: "java",
                        is_free: 0
                },
                {
                        id: 2,
                        course: "Vue",
                        is_free: 0
                },
                {
                        id: 3,
                        course: "GoLang",
                        is_free: 1
                },
                {
                        id: 4,
                        course: "React",
                        is_free: 0
                },
        ];

const ret = data.myEvery(item => ite.is_free === 0);
console.log(ret)    //false
 

Array.prototype.some

和every相反,some: 是不是至少有1个元素通过了被提供的函数测试,同样也返回一个bool值
some接受的参数和every一样,cb回调函数接受的参数也是一样,我们直接开写

//some特点
// 1. 当有一个满足cb回调函数的条件时就停止遍历
// 2. 返回一个bool值
Array.prototype.myEvery = function(cb) {
    let _data = this,
          len = _data.length,
          arg1 = arguments[1],
          ret = false;
    for(let i = 0; i < len; i++) {
        if(cb.apply(thisArg, [_data[i], i, _data])) {
            ret = true;
            break;
        }
    }
    return ret;
}
const ret = data.mySome(item => item.is_free === 1);
//结果:ret = true

Array.prototype.reduce

终于到了最重要的reduce了,有个大佬说你的项目用不到reduce就不算是好的项目。
先来看个小例子热身

const arr = [1,3,5,7,9];
const initVal = [];
const newArr = arr.reduce((prev, item, index) => {
    console.log(prev === initVal);
    prev.push(item);
    return prev;
}, initVal);
console.log(newArr = initVal);
执行结果: 打印了5遍true, 这就说明prev开始等于由initVal,并且改变的时候两者同时更新  
最后执行打印reduce函数返回值与initVal === newArr --->true

既然知道了回调函数中的第一个参数prev就是initVal,并且每次return后才会加入到initVal,再来个小例子,用reduce实现过滤的功能

onst data = [
                {
                    id: 1,
                    course: "java",
                    is_free: 0
                },
                {
                    id: 2,
                    course: "Vue",
                    is_free: 0
                },
                {
                    id: 3,
                    course: "GoLang",
                    is_free: 1
                },
                {
                    id: 4,
                    course: "React",
                    is_free: 0
                },
	];
        const initVal = [];
        const newData = data.reduce((prev, item) => {
            if(item.is_free === 1) {
                prev.push(item);
            }
            return prev;
        }, initVal);
        console.log(newData);
        
//结果为: [0: {id: 3, course: "GoLang", is_free: 1}]

reduce的实现

// const ret = arr.reduce(cb(prev, currentEle, index, array), initVal);
                          cb-> 执行数组中每个值
                             prev-> 它是上一次调用回调时返回的累积值,
                             currentEle-> 数组中正在处理的元素
                             index-> 数组中正在元素的索引
                             array-> 数组中正在处理的当前元素的索引
                          initVal-> 作为第一次调用 callback函数时的第一个参数的值,如果没有提供初始值,将使用数组中的第一个元素
Array.prototype.myReduce = function(cb, initVal) {
    const _data = this,
          len = _data.length;
    for(let i = 0; i < len; i++) {
        const item = _data[i];
        if(!initVal) {
            initVal = cb(_data[0], item, i, _data);
        } else {
            initVal = cb(initVal, item, i, _data);
        }
    }
    return initVal;
}
//用个小例子来测试  
//把下面的字符串解析成对象
{
    BAIDUID: "8fd7re09s",
    TAOBAOID: "9dfhj90nsd",
    ...
}
const str = "BAIDUID=8fd7re09s; TAOBAOID=9dfhj90nsd; PSIND=nas98asdj";
const arr = str.split("; ");
const initVal = {};
const ret = arr.myReduce((prev, item) => {
    const [key, val] = item.split("=");
    prev[key] = val;
    return prev;
}, initVal);
//打印: {BAIDUID: "8fd7re09s", TAOBAOID: "9dfhj90nsd", PSIND: "nas98asdj"}

Array.prototype.reduceRight

用法不多说,直接开写

Array.prototype.myReduceRight = function(cb, initVal) {
    const _data = this,
          len = _data.length;
    for(let i = len - 1; i >= 0; i--) {
        initVal = cb(initVal, _data[i], i, _data);
    }
    return initVal;
}