手写bind、call、apply(简洁代码)

118 阅读1分钟
  • 功能上 与 原生 保持一致
    • 参数判断,边界处理等不保证(这样代码少,方便理解)。
  • 符合ecma的标准

尽量满足2个标准,除非无法实现

bind

Function.prototype.myBind = function (context, ...partArgs) {
    let fn = this
    let prototype = fn.prototype

    function boundFn(...args) {
        // new
        if (this instanceof boundFn) {
            return new fn(...partArgs, ...args)
        }
        // 函数调用
        return fn.call(context, ...partArgs, ...args)
    }

    // 用于instanceof
    // ecma规定值应该为undefined,但实现不了
    boundFn.prototype = prototype
    return boundFn
}

call

  • 在obj上调用函数,函数的this就会指向obj
Function.prototype.myCall = function (context, ...args) {
    // null||undefined context指向window
    // Objet:基本类型封装成对象, 对象类型直接返回
    context = Object(context ?? window);
    let fn = this;
    let id = Symbol();
    // 减少对context的侵入
    Object.defineProperty(context, id, {configurable: true, enumerable: false, writable: false, value: fn})
    try {
        return context[id](...args); //执行fn
    } finally {
        delete context[id]; //删除方法
    }
};

apply

Function.prototype.myApply = function (context, args) {
    return this.myCall(context, ...args)
};

兼容es3

上面的代码只支持ES5,对于Symbol,函数...传参进行兼容处理。

Symbol

function mySymbol(obj) {
    let id = Math.round().toString(32).slice(2, 10)
    return Object.hasOwnProperty(obj, id) ? mySymbol(obj) : id
}

函数传参

function callFn(fn, args) {
    let call = new Function('fn', 'args', 'return fn(args.joun)')
    return call(fn, args)
}