原生API的手动实现

Posted by Mars . Modified at

部分原生API的手动实现

一、数组 reduce()方法

二、对象 深拷贝

一、数组Array

1. reduce()方法

function myReduce(arr, fn = (accu, item, index, array) => {}, init) {
// 判断arr是否是数组;
    if (Object.prototype.toString.call(arr).slice(8,-1) !== 'Array') {
        throw new Error('Must be called to an array.');
    }
// 判断fn是否是函数;
    if (Object.prototype.toString.call(fn).slice(8,-1) !== 'Function') {
        throw new Error('No callback function.');
    }
// 当没有提供初始值,初始值设为arr[0],并且从1位置开始遍历;
// 提供了初始值,从0位置开始遍历;
    let start = 0;
    if (init === undefined) {
        if (arr.length === 0) throw new Error('Call reduce with no init value to an empty array.');
        init = arr[0];
        start = 1;
    }

// 执行函数,迭代修改init。
    for (let i=start; i<arr.length; i++) {
        init = fn(init, arr[i], i, arr);
    }

// 返回最终结果。
    return init;
}

2. map()方法

// for循环实现
function myMap(arr, fn = i => i) {
    if (!Array.isArray(arr)) throw new Error(`myMap must be called on Array instance.`);
    if (typeof fn !== 'function') throw new Error(`the second parameter must be function.`);
    let res = new Array(arr.length);
    for (let i=0; i<arr.length; i++) {
        res[i] = fn(arr[i], i, arr);
    }
    return res;
}

// reduce方法实现
function myMap2(arr, fn = i => i) {
    if (!Array.isArray(arr)) throw new Error(`myMap must be called on Array instance.`);
    if (typeof fn !== 'function') throw new Error(`the second parameter must be function.`);
    return arr.reduce((res, item, index, array) => {
        return [...res, fn(item, index, array)];
    }, []);
}

3. filter()方法

function myFilter(arr, fn) {
    if (Object.prototype.toString.call(arr).slice(8,-1) !== 'Array') {
        throw new Error('Must be called to an array.');
    }
    if (Object.prototype.toString.call(fn).slice(8,-1) !== 'Function') {
        throw new Error('No callback function.');
    }

    let res = [];
    for (let i=0; i<arr.length; i++) {
        let cur = fn(arr[i], i, arr);
        if (cur) res.push(arr[i]);
    }

    return res;
}

二、对象

1. call、apply 和 bind

1.1 call 和 apply

基本思路:

  1. 如果传入上下文context,则把方法挂载到context,如果没有则挂载到window;
  2. 为临时挂载的方法设置一个独一无二的symbol键名;
  3. 通过context调用this指向的方法;
  4. 调用完成,删除context上临时挂载的方法。
// call.js
Function.prototype.myCall = function(context, ...args) {
    let c = context || window;
    let sym = Symbol('callFn');
    context[sym] = this;
    let res = context[sym](...args);
    delete context[sym];
    return res;
};

apply相似,把第二个参数设置为数组即可。

1.2 bind

基本思路:

  1. 传入一个上下文context,如果没有则为window;
  2. myBind内部this指向需要被绑定的原始函数fn;
  3. 返回一个函数binded,通过fn.call(context),手动指定其内部的this指向;
  4. 返回这个函数即可。
// bind.js
Function.prototype.myBind = function(context) {
    let c = context || window;
    let fn = this;
    let binded = function(){
        fn.call(c);
    }
    return binded;
}

2. 深拷贝

Keywords: JavaScript
previousPost nextPost
已经有 1000000 个小伙伴看完了这篇推文。