极客时间返利平台,你可以在上边通过山月的链接购买课程,并添加我的微信 (shanyue94) 领取返现。
每天晚上九点 B站讲解前端工程化直播,并解答相关问题。

# JS 中如何实现 call/apply

Issue

欢迎在 Gtihub Issue 中回答此问题: Issue 674 (opens new window)

const call = (fn, thisObj, ...args) => {
  thisObj.fn = fn;
  const r = thisObj.fn(...args);
  delete thisObj.fn;
  return r;
};

Author

回答者: wussss (opens new window)

bind/softBind/apply/call 都是 this 显式绑定的方法

  • bind 会返回一个硬绑定的新函数,新函数会使用指定的第一个 thisCtx 去调用原始函数,并将其它参数传给原始函数。 硬绑定会降低函数的灵活性,在绑定之后不能通过显式或硬绑定的方式改变 this,只能通过 new 改变
  • softBind 会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就用指定的 thisCtx 去调用函数,否则不会修改 this
  • apply 和 call 功能相同,都是以指定的 thisCtx 和参数去执行方法,并返回原方法的返回值,只是 apply 中参数以数组传递
Function.prototype.myBind = function (ctx = globalThis) {
  const fn = this;
  const args = Array.from(arguments).slice(1);
  function bound() {
    if (this instanceof bound) {
      fn.apply(this, args);
    } else {
      fn.apply(ctx, args);
    }
  }
  bound.prototype = fn.prototype;
  return bound;
};
Function.prototype.mySoftBind = function (ctx = globalThis) {
  const fn = this;
  const args = Array.from(arguments).slice(1);
  function bound() {
    if (!this || this === globalThis) {
      fn.apply(ctx, args);
    } else {
      fn.apply(this, args);
    }
  }
  bound.prototype = fn.prototype;
  return bound;
};
Function.prototype.myCall = function (ctx = globalThis) {
  const args = Array.from(arguments).slice(1);
  const key = Symbol("key");
  ctx[key] = this;
  const res = ctx[key](...args);
  delete ctx[key];
  return res;
};
Function.prototype.myApply = function (ctx = globalThis) {
  const args = arguments[1];
  const key = Symbol("key");
  ctx[key] = this;
  const res = ctx[key](...args);
  delete ctx[key];
  return res;
};
Function.prototype.call = function call(arm, ...args) {
  let fun = this;
  if (typeof fun !== "function") throw TypeError("must is function");
  let armObj = arm;
  if (typeof arm !== "object") {
    armObj = Object(arm);
  }

  let symbolKey = Symbol("tempKey");
  armObj[symbolKey] = fun;
  let result = armObj[symbolKey](...args);
  delete armObj[symbolKey];
  return result;
};

Function.prototype.apply = function call(arm, ...args) {
  let fun = this;
  if (typeof fun !== "function") throw TypeError("must is function");
  let armObj = arm;
  if (typeof arm !== "object") {
    armObj = Object(arm);
  }

  let symbolKey = Symbol("tempKey");
  armObj[symbolKey] = fun;
  let result = armObj[symbolKey](args);
  delete armObj[symbolKey];
  return result;
};

// TODO 完善bind 这里其实还有很多问题
Function.prototype.bind = function aBind(that, ...args) {
  let armFun = this;
  if (typeof armFun !== "function") throw TypeError("must a function");
  function BoundFun(...other) {
    if (new.target) {
      return new armFun(...args, ...other);
    } else {
      return armFun.call(that, ...args, ...other);
    }
  }
  BoundFun.__proto__ = armFun.__proto__;
  BoundFun.prototype = undefined;

  return BoundFun;
};

Author

回答者: iceycc (opens new window)

let person1 = {
  name: "Tom",
  sayHi(to, ...args) {
    console.log(
      `Hi,${to}, my name is ${this.name}${args && args.toString()}`
    );
  },
};
person1.sayHi();

let person2 = {
  name: "Jerry",
};
// call
person1.sayHi.call(person2, "Heydi");
// apply
person1.sayHi.apply(person2, ["Heydi"]);
// bind
let sayHiToJark = person1.sayHi.bind(person2, "Heydi"); // 柯里化
sayHiToJark("Wellcom to you");
// my call
Function.prototype.myCall = function (ctx, ...args) {
  let fn = this;
  if (typeof fn !== "function") throw TypeError("must is fucntion");
  let thisObj = ctx;
  if (typeof ctx !== "object") {
    thisObj = Object(ctx);
  }
  const key = Symbol("key");
  thisObj[key] = fn;
  const res = thisObj[key](...args);
  delete thisObj[key];
  return res;
};
person1.sayHi.myCall(person2, "Tim");
// my apply
Function.prototype.myApply = function (ctx, args) {
  let fn = this;
  if (typeof fn !== "function") throw TypeError("must is fucntion");
  let thisObj = ctx;
  if (typeof ctx !== "object") {
    thisObj = Object(ctx);
  }
  if (!Array.isArray(args)) throw TypeError("must is array");
  const key = Symbol("key");
  thisObj[key] = fn;
  const res = thisObj[key](...args);
  delete thisObj[key];
  return res;
};
person1.sayHi.myApply(person2, ["Tim"]);
// my bind
Function.prototype.myBind = function (ctx, ...args) {
  const fn = this;
  return function (...args2) {
    const key = Symbol("key");
    ctx[key] = fn;
    const res = ctx[key](...args, ...args2);
    delete ctx[key];
    return res;
  };
};
let sayHiToMary = person1.sayHi.bind(person2, "Mary");
sayHiToMary("Wellcom to you");
Last Updated: 11/27/2021, 10:11:48 AM