call、apply、bind的实现

87 阅读1分钟

1、call、apply的实现的最终目的是为了让函数在被绑定的对象上执行。

例如:

    var obj = {a: 1};
    function fn(b) {
        console.log(b);
        console.log(this.a)
    };
    obj.fn = fn;
    obj.fn();

问题是这样会在obj中,添加一个多于的对象。所以我们删除它:

    delete obj.fn;

call实现代码:

    Function.prototype.myCall = function(context) {
        // fn.myCall(obj), fn不是一个函数时,抛出异常
        if (typeof this != 'function') {
            throw Error('type error');
        }
        // arguments是一个维数组,没有slice方法
        let args = Array.prototype.slice.call(arguments, 1);
        // 如果fn.myCall(obj)的obj不存在,则在全局运行fn
        context = context || window;
        context.fn = this;
        let result = context.fn(...args);
        // 删除obj中的fn
        delete context.fn;
        return result;
    }

apply实现代码:

    Function.prototype.myApply = function(context) {
        // fn.myApply(obj), fn不是一个函数时,抛出异常
        if (typeof this != 'function') {
            throw Error('type error');
        }
        // arguments是一个维数组,没有slice方法
        let args = Array.prototype.slice.call(arguments, 1);
        // apply第二参数是一个数组
        args = args?.[0] ?? []
        // 如果fn.myApply(obj)的obj不存在,则在全局运行fn
        context = context || window;
        context.fn = this;
        let result = context.fn(...args);
        // 删除obj中的fn
        delete context.fn;
        return result;
    }

2、bind和apply和call有点不一样,它不会立即执行,bind会返回一个函数。

    Function.prototype.myBind = function(context) {
        // fn.myBind(obj), fn不是一个函数时,抛出异常
        if (typeof this != 'function') {
            throw Error('type error');
        }
        // arguments是一个维数组,没有slice方法
        let args = Array.prototype.slice.call(arguments, 1);
        // 保存 fn
        let that = this;
        // 创建一个fn
        let Fn = function() {
            // arguments为当前函数的的arguments
            let fnArgs = Array.prototype.slice.call(arguments, 0);
            // 如果 fn在this的原型链上,即this指向 new Fn()①
            that.apply(this instanceof that ? this : context, [...args, ...fnArgs]);
        }
        // 让Fn的原型指向fn的原型,注释①的前置条件。
        Fn.prototype = Object.create(that.prototype);
        return Fn;
    }