【前端必会手写】call和apply方法

call和apply都是Function原型prototype上的方法。
用于改变this指向。
两者不同点在于传参。
call需要传递散参,而apply需要传递一个参数数组。
这两个方法相比bind有一个区别,在于这两个方法是直接调用的,而bind方法是返回一个函数,而这个函数的this指向是明确的。

我们先来看看call方法有哪些注意点。

function a(...args) {
  console.log(this, args);
}

a.call({ a: 1 }, 1, 2, 3);
// { a: 1 } [ 1, 2, 3 ]

当然如果我们绑定的this如果不是对象的话,那么其实call方法也会转成对象

function a(...args) {
  console.log(this, args);
}

a.call(1, 1, 2, 3); // [Number: 1] [ 1, 2, 3 ]

大概知道他的一些细节后,我们来手写一下call方法

Function.prototype.myCall = function (_this, ...args) {
  // call方法可以用来改变方法调用的this指向
  const func = this;
  if (typeof func !== "function") {
    // 类型判断
    throw TypeError("this is not a function");
  }
  if (typeof _this !== "object") {
    // 如果绑定的this不是一个对象
    _this = Object(_this);
  }
  _this.fn = func;
  const result = _this.fn(...args);
  // 这里在_this上添加了一个属性,需要删除
  delete _this.fn;
  return result;
};

这里我们测试一下方法的实现是否和call方法一致

function a(...args) {
  console.log(this, args);
}

a.myCall(1, 1, 2, 3);
// [Number: 1] { fn: [Function: a] } [ 1, 2, 3 ]


function a(...args) {
  console.log(this, args);
}

a.myCall({ a: 1 }, 1, 2, 3);
// { a: 1, fn: [Function: a] } [ 1, 2, 3 ]

这里其实有一个小瑕疵,就是如果函数中有用到this中的一个属性fn那么就会有问题了,并且打印出来也会有问题
但是基本功能已经很好了
但如果你是一个完美主义的人
那我们不妨继续完善代码

Function.prototype.myCall = function (_this, ...args) {
  // call方法可以用来改变方法调用的this指向
  const func = this;
  if (typeof func !== "function") {
    // 类型判断
    throw TypeError("this is not a function");
  }
  if (typeof _this !== "object") {
    // 如果绑定的this不是一个对象
    _this = Object(_this);
  }
  const random = Math.random().toString(16).substring(2);
  _this["fn" + random] = func;
  const result = _this["fn" + random](...args);
  // 这里在_this上添加了一个属性,需要删除
  delete _this['fn' + random];
  return result;
};

生成一个随机字符串来防止属性重复

啥?还不完美吗?

Function.prototype.myCall = function (_this, ...args) {
  // call方法可以用来改变方法调用的this指向
  const func = this;
  if (typeof func !== "function") {
    // 类型判断
    throw TypeError("this is not a function");
  }
  if (typeof _this !== "object") {
    // 如果绑定的this不是一个对象
    _this = Object(_this);
  }
  const random = Math.random().toString(16).substring(2);
  Object.defineProperty(_this, "fn" + random, {
    value: func,
    enumerable: false,
  });
  const result = _this["fn" + random](...args);
  // 这里在_this上添加了一个属性,需要删除
  delete _this.fn;
  return result;
};
function a(...args) {
  console.log(this, args);
}

a.myCall({ a: 1 }, 1, 2, 3);
// { a: 1 } [ 1, 2, 3 ]
Function.prototype.myApply = function (_this, args) {
  const func = this;
  if (typeof func !== "function") {
    // 类型判断
    throw TypeError("this is not a function");
  }
  if (!Array.isArray(args)) {
    throw TypeError("arg is not a array");
  }
  return func.myCall(_this, ...args);
};

我的github有更多的前端手写代码。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值