js重学之深入理解call, apply, bind(原理,重写)

39 阅读1分钟

作用:这三个方法都是用来改变this指向

不同点:

  • call和apply 是立即执行函数,但是bind会返回一个改变了this指向的函数,手动执行。
  • call和bind的接受参数方式是一个个参数,apply接受参数是一个数组。

作用相同为什么搞了三个方法?不同场景应用方式不同

以下代码可以看出三种方法的使用不同

let obj = { 
    name: this.name, 
    objAge: this.age, 
    myLove: function (fm, t) {
        console.log(this.name + "年龄" + this.age, "来自" + fm + "去往" + t); }, 
}; 
let obj1 = { name: "huang", age: 22, }; 
obj.myLove.call(obj1, "达州", "成都"); //huang年龄22来自达州去往成都
obj.myLove.apply(obj1, ["达州", "成都"]); //huang年龄22来自达州去往成 obj.myLove.bind(obj1, "达州", "成都")(); // huang年龄22来自达州去往成都

重写call方法

    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}

// 一般面试选这种方式就行,
Function.prototype.myCall1 = function(context, ...args) {
            context = context || window;
            const key = Symbol('key');
            context[key] = this;

            const result = context[key](...args);
            return result
        }

重写apply方法

    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}

// 第二种方式
Function.prototype.myApply = function(context, args) {
            context = context || window;
            const key = Symbol('key');
            context[key] = this;

            const result = context[key](...args);
            return result
        }

重写bind方法

Function.prototype.myBind = function(context, ...bindArgs) {
  // 1. 保存原始函数引用
  const originalFunc = this;

  // 2. 创建绑定函数(最终返回的函数)
  const boundFunc = function(...callArgs) {
    // 3. 检测是否通过 new 调用
    const isNewCall = this instanceof boundFunc;

    // 4. 确定执行上下文
    const thisContext = isNewCall ? this : (context || window);

    // 5. 合并参数(预置参数 + 调用时参数)
    const finalArgs = bindArgs.concat(callArgs);

    // 6. 执行原函数并返回结果
    return originalFunc.apply(thisContext, finalArgs);
  };

  // 7. 维护原型链(处理 new 操作符)
  if (originalFunc.prototype) {
    const EmptyFunc = function() {};
    EmptyFunc.prototype = originalFunc.prototype;
    boundFunc.prototype = new EmptyFunc();
  }

  // 8. 返回绑定函数
  return boundFunc;
};
```