javascript 中 bind、call和apply的区别

😉 你好呀,我是爱编程的Sherry,很高兴在这里遇见你!我是一名拥有十多年开发经验的前端工程师。这一路走来,面对困难时也曾感到迷茫,凭借不懈的努力和坚持,重新找到了前进的方向。我的人生格言是——认准方向,坚持不懈,你终将迎来辉煌!欢迎关注我😁,我将在这里分享工作中积累的经验和心得,希望我的分享能够给你带来帮助。

引言

在 JavaScript 中,bindcallapply 都用于显式地改变函数执行时的 this 指向,但它们的使用方式和行为略有不同。我们来逐个分析这三者的区别,以及如何手动实现一个 bind


1. callapply 的区别

相同点

callapply 都是立即调用函数,并且可以改变函数内部 this 的指向。

不同点

call:传递参数时使用的是参数列表
apply:传递参数时使用的是数组

举个栗子:
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

// 使用 call
greet.call(person, 'Hello', '!'); // 输出: Hello, Alice!

// 使用 apply
greet.apply(person, ['Hi', '?']); // 输出: Hi, Alice?

在上面的例子中,callapply 都将 this 指向 person 对象,并传递参数来执行函数。

2. bind 的区别

bind 不会立即调用函数,它返回一个新的函数,并且该函数的 this 永远绑定为指定的对象。绑定后的函数可以稍后执行,并且可以在调用时传递参数。

举个栗子:
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

// 使用 bind
const greetPerson = greet.bind(person, 'Hello');
greetPerson('!'); // 输出: Hello, Alice!

在上面的例子中,greet.bind(person, 'Hello') 返回了一个绑定了 thisperson 的新函数 greetPerson,并且在调用时传递了第二个参数 '!'

3. 区别总结

特性callapplybind
是否立即调用否,返回绑定了 this 的新函数
参数传递方式参数列表参数数组返回的新函数可以接收后续参数
this 指向显式传递的对象显式传递的对象永久绑定为指定的对象

4. 如何实现一个 bind 方法

手动实现 bind 方法其实涉及到创建一个新的函数,并且该函数会将 this 绑定到指定的对象上,同时保留传递的参数。bind 还需要处理柯里化,即调用时传递参数与绑定时传递的参数要合并起来。以下是一个手动实现 bind 的简单版本:

Function.prototype.myBind = function(context, ...args) {
  const fn = this; // 保存原函数的引用
  
  return function(...newArgs) {
    // 将上下文绑定到函数,并合并绑定时的参数和调用时的参数
    return fn.apply(context, [...args, ...newArgs]);
  };
};
说明:

保存函数引用const fn = this 保存了原始函数的引用,因为 bind 调用的是函数本身,所以 this 就是原始函数。
返回新函数myBind 方法返回了一个新的函数,这个函数在执行时会将 this 绑定为传入的 context,并且传递参数。
参数合并:我们在 bind 调用时可以预先传递一些参数,也可以在新函数调用时传递剩余的参数,因此需要将绑定时的参数 args 和调用时的新参数 newArgs 合并起来。

使用自定义 myBind 的例子:
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

const greetPerson = greet.myBind(person, 'Hello');
greetPerson('!'); // 输出: Hello, Alice!

在这个例子中,myBind 的实现和原生 bind 的行为一样,成功绑定了 thisperson 对象。

5. 更完整的 bind 实现

bind 的一个关键特性是新返回的函数如果作为构造函数使用,应该忽略 bind 时的 this 绑定,而使用新创建的实例对象作为 this。这是 bind 更复杂的一个特性,下面是一个完整的 bind 实现:

Function.prototype.myBind = function(context, ...args) {
  const fn = this; // 保存原函数的引用
  
  return function(...newArgs) {
    // 如果作为构造函数使用,this 应该指向新创建的实例对象,而不是绑定的 context
    const isNewInstance = this instanceof fn;
    
    return fn.apply(isNewInstance ? this : context, [...args, ...newArgs]);
  };
};
说明:

检查是否作为构造函数使用this instanceof fn 用来判断返回的新函数是否被用作构造函数。如果是构造函数,this 应该指向新创建的实例,而不是 context
动态 this 绑定:如果是构造函数调用,this 会指向新对象,否则会绑定到 context

举个栗子:
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const createPerson = Person.myBind(null, 'Alice');

const person1 = new createPerson(25);
console.log(person1.name); // 输出: Alice
console.log(person1.age);  // 输出: 25

在这个例子中,myBind 支持作为构造函数使用,成功创建了 person1 实例,this 指向新对象而不是 null

6. 总结

callapply 都会立即调用函数,区别在于参数的传递方式:call 使用参数列表,而 apply 使用数组。
bind 返回一个新的函数,并将 this 永久绑定到指定的对象上,可以稍后调用,并支持柯里化参数传递。
自己实现 bind 主要涉及到:返回一个新函数,保留和合并参数,支持作为构造函数调用时的 this 绑定。

如果你还有其他问题欢迎评论区给我留言哦!探索技术的领域,一起加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sherry Tian

打赏1元鼓励作者

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值