Array方法实现(forEach, map, filter, some, every, indexOf, lastIndexOf, reduce)

forEach

const root = this;

Array.prototype.myForEach = function(fn, context) {
    // 可选参数, 设置回调中的this指向
    context = context || root;

    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');

    for (let i = 0, len = this.length; i < len; i++) {
        // 不对不存在或已经删除的属性(索引)执行函数 
        if (Object.prototype.hasOwnProperty.call(this, i)) {
            fn.call(context, this[i], i, this);
        }
    }
}

const names = ['Wango', 'Lily', 'Peter'];
names.myForEach((val, idx, all) => {
    console.log(idx, val, all);
});

// 0 Wango [ 'Wango', 'Lily', 'Peter'  ]
// 1 Lily [ 'Wango', 'Lily', 'Peter'  ]
// 2 Peter [ 'Wango', 'Lily', 'Peter'  ]

delete names[2];
console.log(names.length);
// 3

names.myForEach((val, idx, all) => {
    console.log(idx, val, all);
});
// 0 Wango [ 'Wango', 'Lily', <1 empty item>  ]
// 1 Lily [ 'Wango', 'Lily', <1 empty item> ]

map

Array.prototype.myMap = function(fn, context) {
    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');
    
    const result = Array(this.length);
    // 类型判断, context默认值等交由forEach循环处理,
    // map只需要保存结果并返回
    this.forEach(function(val, idx, all) {
        result[idx] = fn.call(this, val, idx, all);
    }, context);

    return result;
}

const nums1 = [1, 2, 3, 4];

const nums2 = nums1.myMap((val, idx, all) => {
    return val * val;
});

console.log(nums1);
// [ 1, 2, 3, 4  ]
console.log(nums2);
// [ 1, 4, 9, 16  ]

filter

Array.prototype.myFilter = function(fn, context) {
    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');

    const result = [];
    this.forEach(function(val, idx, all) {
        // 将满足条件的结果加入新数组
        fn.call(this, val, idx, all) && result.push(val);
    }, context);

    return result;
}

const num1 = [1, 5, 3, 6, 7, 2, 3, 6, 9, 0];
const num2 = num1.myFilter(val => val > 5);

console.log(num1);
// [1, 5, 3, 6, 7, 2, 3, 6, 9, 0]
console.log(num2);
// [ 6, 7, 6, 9  ]

some

const root = this;

Array.prototype.mySome = function(fn, context) {
    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');
    
    context = context || root;
    // forEach无法正常中断循环, 使用for代替
    // 只要有一个满足条件即可返回true不需要全部遍历
    for (let i = 0, len = this.length; i < len; i++) {
        if (fn.call(context, this[i], i, this)) return true;
    }
    return false;
}

const nums1 = [1, -3, 2, 3, 4, -1];
console.log(nums1.mySome(val => val < 0));
// true
console.log(nums1.mySome(val => val === undefined));
// false

delete nums1[1];
console.log(nums1);
// [ 1, <1 empty item>, 2, 3, 4, -1  ]

console.log(nums1.mySome(val => val === undefined));
// true

every

const root = this;

Array.prototype.myEveny = function(fn, context) {
    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');

    context = context || root;
    // forEach无法正常中断循环, 使用for代替
    // 只要有一个不满足条件即可返回false不需要全部遍历
    for (let i = 0, len = this.length; i < len; i++) {
        if (!fn.call(context, this[i], i, this)) return false;
    }
    return true;
}

const nums1 = [2, -1, 3, 5, 1, 0];
console.log(nums1.myEveny(val => typeof val === 'number'));
// true

indexOf

Array.prototype.myIndexOf = function(val, fromIndex) {
    // 只要是数字或者可以被自动转换为数字即为有效
    // 否则使用默认起始点0
    fromIndex = fromIndex * 1 || 0;
    // 索引从小到大顺序查找, 返回第一个查找到的索引
    for (let i = fromIndex, len = this.length; i < len; i++) {
        if (this[i] === val) return i;
    }
    return -1;
}

const names = ['Wango', 'Lily', 'Lily', 'Peter'];
console.log(names.myIndexOf('Lily'));
// 1
console.log(names.myIndexOf('Anna'));
// -1
console.log(names.myIndexOf('Lily', 1));
// 1
console.log(names.myIndexOf('Lily', 2));
// 2
console.log(names.myIndexOf('Lily', '2'));
// 2
console.log(names.myIndexOf('Lily', -1));
// 1

lastIndexOf

Array.prototype.myLastIndexOf = function(val, fromIndex) {
	const len = this.length;
	// 起始点为数组长度减一
    fromIndex = fromIndex * 1 || len - 1;
	// 从大到小逆序查找,返回查找到的第一个元素的索引
    for (let i = fromIndex; i > 0; i--) {
        if (this[i] === val) return i;
    }
    return -1;
}

const names = ['Wango', 'Lily', 'Lily', 'Peter'];
console.log(names.myLastIndexOf('Lily'));
// 2
console.log(names.myLastIndexOf('Anna'));
// -1
console.log(names.myLastIndexOf('Lily', 1));
// 1
console.log(names.myLastIndexOf('Lily', 2));
// 2
console.log(names.myLastIndexOf('Lily``', '2'));
// 2
console.log(names.myLastIndexOf('Lily', -1));
// 1

reduce

Array.prototype.myReduce = function(fn, initVal) {

    if (typeof fn !== 'function') throw new TypeError('A function is expected on parameter 1');
    // 若initVal不是或不能自动转换为数字
    // 则初始值为第一个元素
    let pre = +initVal || this[0];
    // 有初始值时当前值从0开始
    // 没有初始值时当前值从1开始(0被上面pre拿走了)
    for (let i = +initVal ? -1 : 0, len = this.length; i < len; i++) {
        // 当下一个值不存在即数组循环完毕后, 返回累计结果
        if (!this[i + 1]) {
            return pre;
        }
        pre = fn.call(this, pre, this[i + 1], i, this);
    }
}

const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];

console.log(nums.myReduce((a, b) => a + b));
// 45
console.log(nums.myReduce((a, b) => a + b, 10));
// 55
console.log(nums.myReduce((a, b) => a + b, 'aa'));
// 45
console.log(nums.myReduce((a, b) => a + b, '10'));
// 55
console.log([[1, 2, 3], [4, 5, 6]].myReduce((a, b) => a.concat(b)));
// [ 1, 2, 3, 4, 5, 6 ]

参考网址:ES5中新增的Array方法详细说明

posted @ 2021-02-03 17:02  LiuWango  阅读(97)  评论(0编辑  收藏  举报