深入JS系列:call、apply、new的实现

110 阅读2分钟

建议阅读之前,如果有兴趣,可以自己打印出new, call, apply的结果,然后自己思考后,实现一下,相信不管你自己实现几次都是有所收获的,本人之前也实现过几次,但是之后就淡忘了,因此输出为一篇文章,一方面方便回顾,另一方面文章代码中也做了一些注释,方便对这个不了解或者淡忘的同学学习回顾,我也相信大家会有收获。

自己实现一版之后,看了很多前辈的实现方式,发现每一种实现方式,或多或少存在一些缺陷包括我自己实现的,本文实现将优化一些已知的实现方式,站在巨人的肩上,就不废话了,直接上代码,细节部分已注释。

  1. call实现
Function.prototype.mycall = function() {
    var fun = this;
    // 为什么要写成[].shift.call呢?因为arguments是一个类数组,这样操作就可以将arguments转化为一个数组
    var thisArg = [].shift.call(arguments) || window;
    var argLength = arguments.length;
    var argArray = [];
    // 既然是实现就要实现的彻底,这样的就不用了
    // const symbol = Symbol();
    var symbol = Math.floor(Math.random() * 10e7);

    // 可有可无,在没有ts的约束下,这里为了不出问题
    if (typeof fun !== 'function') {
        throw new Error(this + '.call is not a function');
    }

    // 参数不定长解决
    for (var i = 0; i < argLength; i++) {
        argArray.push('arguments[' + i + ']');
    }

    thisArg[symbol] = fun;

    var result = eval('thisArg[symbol](' + argArray.join(',') + ')');

    // 删除多加的属性
    delete thisArg[symbol];

    // 返回函数执行结果
    return result;
}
  1. apply实现
Function.prototype.myapply = function() {
    var fun = this;
    // 为什么要写成[].shift.call呢?因为arguments是一个类数组,这样操作就可以将arguments转化为一个数组
    var thisArg = arguments[0] || window;
    var arg = arguments[1];
    // 既然是实现就要实现的彻底,这样的就不用了
    // const symbol = Symbol();
    var symbol = Math.floor(Math.random() * 10e7);

    // 可有可无,在没有ts的约束下,这里为了不出问题
    if (typeof fun !== 'function') {
        throw new Error(this + '.call is not a function');
    }

    if (!(arg instanceof Array) && arg != undefined) {
        throw new TypeError('CreateListFromArrayLike called on non-object');
    }

    thisArg[symbol] = fun;

    var argString = '';

    // 既然arg是数组,那为什么还要做这个操作呢,为什么不直接arg.join()呢?
    // 因为如果直接arg.join()的话arg中的string类型将转化为一个变量名,这里找不到对于的变量名就会带入错误
    if (arg instanceof Array) {
        var argArray = [];

        for (var i = 0; i < arg.length; i++) {
            argArray.push('arg[' + i + ']');
        }

        argString = argArray.join();
    }

    var result = eval('thisArg[symbol](' + argString + ')');

    // 删除多加的属性
    delete thisArg[symbol];

    // 返回函数执行结果
    return result;
}
  1. new的实现方式
function mynew() {
    const Fn = [].shift.call(arguments);

    if (typeof Fn !== 'function') {
        throw new Error('Uncaught TypeError: ' + Fn + ' is not a constructor');
    }

    const obj = Object.create(Fn.prototype);

    const result = Fn.myapply(obj, [].slice.call(arguments));

    return result instanceof Object ? result : obj;
}

以上实现并不完美,当局者迷,如果看出哪里有优化的空间可以发表评论,共同探讨,一起进步。