JavaScript中call、apply、bind详解及手写实现

首先,我们都知道 JavaScript 中的 this 指向分为软绑定及硬绑定。并且 this 的指向与执行上下文有关系。
在全局执行上下文中,this 指向 全局对象(window/global),在对象调用其内部方法时,this 指向调用的地方,这就称为软绑定JavaScript中this指向详解
那么如何使用硬绑定呢?这就需要使用 JavaScript 中内置的 call/aplly/bind 方法来实现了。

1.call

1.1.基本用法

  • Mdn 给出的介绍如下。翻译过来就是 :
    • call 方法会将 this 指向传入的 第一个参数,
    • call 方法可以接收多个参数
    • 调用函数执行

在这里插入图片描述

1.2.call使用的例子

软绑定的时候我们是这么使用的:

console.log(this); // this -> window
let obj = {
  name: "刘德华",
  say: function () {
    console.log("say");
    console.log(this); // this -> obj
  },
};
obj.say();

输出结果如下:

在这里插入图片描述
那么现在我们有一个新的对象,如何将 this 挂载到新对象上呢?

let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args); // 此处通过解构赋值拿到所有的参数
    console.log(this); // this -> o
  },
};
// 此处调用 call,第一个参数就是要绑定的 this,后面就是要传递的参数
obj.say.call(o, o.sing, 1, 2, 3);

可以看到此时打印的 this已经不是 obj 了,而是新定义的 o:

在这里插入图片描述

1.3.自己实现一个 call 方法

思路:

  • Function.prototype 上定义一个 myCall 方法,并且 obj.say 调用自定义的 myCall 方法
  • 接收第一个参数,就是传递进来要硬绑定的新对象
  • 在新对象内部创建一个 fn 函数,将 当前 this 赋值给 fn 函数(当前thisobj.say
  • 调用新对象中的 fn 函数执行(即调用obj.say执行)
  • 完成后将新对象中的 fn 函数删除(避免占用内存)
Function.prototype.myCall = function (context, ...args) {
  console.log(this); // this -> obj.say
  console.log(args); // 拿到参数做些什么
  context.fn = this;
  context.fn();
  delete context.fn;
};
let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function () {
    console.log(this); // this -> o
  },
};
obj.say.myCall(o, o.sing, 1, 2, 3);

在这里插入图片描述

这样实现有一个问题,当我们第一个参数传递的是 null 的时候是会报错的:

在这里插入图片描述
原生的 call 方法是不会报这个错误的,因此需要对参数进行兜底处理:

在这里插入图片描述
当然了,还应该将 接收的多个参数,传递给 obj.say 方法使用:

Function.prototype.myCall = function (context, ...args) {
  console.log(this); // this -> obj.say
  context = context ? context : window; // 参数兜底处理
  context.fn = this;
  context.fn(...args); // 将拿到的参数,传递给 obj.say 使用
  delete context.fn; // 完成后就删除,避免浪费内存
};
let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args); // 拿到参数做些什么
    console.log(this); // this -> o
  },
};
obj.say.myCall(null, o.sing, 1, 2, 3);

2.apply

2.1.基本用法

  • 改变 this 指向
  • 传递一个参数数组
  • 调用函数执行
    在这里插入图片描述

2.2. apply使用的例子

let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args);
    console.log(this); // this -> o
  },
};
obj.say.apply(o, [o.sing, 1, 2, 3]);

除了入参不同,其他和 call 没什么区别:

在这里插入图片描述

2.3. 自己实现一个apply方法

Function.prototype.myApply = function (context, args) {
  context = context ? context : window;
  context.fn = this;
  if (!args) {
    context.fn();
  } else if (Array.isArray(args)) {
    context.fn(...args);
  } else {
    return TypeError("args is not a Array");
  }
  delete context.fn;
};
let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args);
    console.log(this); // this -> o
  },
};
obj.say.myApply(o, [o.sing, 1, 2, 3]);

3.bind

3.1.基本使用

  • 改变 this 指向
  • 接收多个参数
  • 返回一个新的函数
    在这里插入图片描述
let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args);
    console.log(this); // this -> o
  },
};
let res = obj.say.bind(o, [o.sing, 1, 2, 3]);
res();

参数可以是任意形式,参数列表/数组都可以:

在这里插入图片描述

3.2.手动实现一个bind

Function.prototype.myBind = function (context, ...args) {
  if (typeof this !== "function") {
    throw new Error(
      "Function.prototype.bind - what is trying to be bound is not callable"
    );
  }

  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);

  var fNOP = function () {};

  var fBound = function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(
      this instanceof fNOP ? this : context,
      args.concat(bindArgs)
    );
  };

  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
};
let o = { sing: "sing" };
// 如何将this指向o呢?
let obj = {
  name: "刘德华",
  say: function (...args) {
    console.log(args);
    console.log(this); // this -> o
  },
};
let res = obj.say.myBind(o, [o.sing, 1, 2, 3]);
res();

4.call/apply/bind的异同

  • 相同点
    • 改变 this 指向
  • 不同点:
    • call:接受的是参数列表
    • apply:接收的是一个参数数组
    • bind:返回一个新的函数,需要再次调用才执行

  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
call、applybind都是用来改变函数的this指向的方法。其,call和apply可以直接调用函数并传递参数,而bind则返回一个新的函数,需要动调用。 具体实方案如下: - call的实: 1. 给想要绑定的对象设置一个属性,并将该属性指向需要调用的函数。 2. 使用该对象调用函数,并传递参数。 3. 结束调用后,删除该属性。 - apply的实: 1. 给想要绑定的对象设置一个属性,并将该属性指向需要调用的函数。 2. 使用该对象调用函数,并传递参数数组。 3. 结束调用后,删除该属性。 - bind的实: 1. 创建一个新的函数,并将原函数作为其的属性保存起来。 2. 当新函数被调用时,将之前绑定的对象作为this,并传递参数。 3. 返回新函数供后续调用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [如何实call、applybind](https://blog.csdn.net/XIAO_A_fighting/article/details/116701887)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [原生JS实 call apply bind](https://download.csdn.net/download/weixin_38628990/14046564)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值