call、apply、bind区别

111 阅读2分钟

一、基础定义与共性

三者均为 Function.prototype 的方法,用于显式绑定函数的 this 上下文,并支持参数传递

二、核心区别对比表

方法立即执行?参数传递方式返回值常见应用场景
call✅ 立即执行thisArg, arg1, arg2, ...(参数列表)函数执行结果继承、单次调用并指定 this
apply✅ 立即执行thisArg, [argsArray](参数数组)函数执行结果参数不确定时(如 Math.max 应用于数组)
bind❌ 不执行thisArg, arg1, arg2, ...(预设参数)新函数(需手动调用)创建固定 this 的回调函数、偏函数

三、代码示例与场景

1. call:逐个传递参数
const person = { name: 'Alice' };

function greet(message) {
  console.log(`${message}, ${this.name}`);
}

greet.call(person, 'Hello'); // "Hello, Alice"
2. apply:参数数组
const numbers = [5, 3, 8, 1];
const max = Math.max.apply(null, numbers); // 等价于 Math.max(5, 3, 8, 1)
console.log(max); // 8
3. bind:创建绑定函数
const counter = {
  count: 0,
  increment() {
    this.count++;
    console.log(this.count);
  }
};

const boundIncrement = counter.increment.bind(counter);
setTimeout(boundIncrement, 1000); // 1(避免 this 指向 window)

四、问题

1. 问:如何用 call 实现继承?
 - 在子类构造函数中通过 `call` 调用父类构造函数,绑定子类的 `this`:  
   ```javascript
   function Parent(name) {
     this.name = name;
   }
   
   function Child(name, age) {
     Parent.call(this, name); // 继承 name 属性
     this.age = age;
   }
   ```  
2. 问:bind 为什么适合作为回调函数?
 - 回调函数常被异步调用(如 `setTimeout`、事件监听),此时 `this` 默认指向全局对象(如 `window`)。  
 - `bind` 可预先绑定 `this`,确保回调执行时上下文正确:  
   ```javascript
   button.addEventListener('click', this.handleClick.bind(this));
   ```  
3. 问:如何用 apply 实现数组展开?
 - 利用 `apply` 将数组作为参数传递给函数:  
   ```javascript
   const arr1 = [1, 2];
   const arr2 = [3, 4];
   arr1.push.apply(arr1, arr2); // 等价于 arr1.push(3, 4)
   console.log(arr1); // [1, 2, 3, 4]
   ```  
4. 问:bind 能否被多次调用?
 - 可以,但后续 `bind` 会**叠加参数**,而非覆盖:  
   ```javascript
   function add(a, b) { return a + b; }
   const add5 = add.bind(null, 5); // 预设 a=5
   const add10 = add5.bind(null, 10); // 预设 a=10(但 a 已被 bind 为 5)
   
   console.log(add5(3)); // 8(5+3)
   console.log(add10(3)); // 15(5+10,第二个参数被忽略)
   ```  

五、性能与最佳实践

1. 性能对比
  • call/apply:直接执行函数,开销较小。
  • bind:创建新函数实例,内存占用更高(尤其频繁调用时)。
2. 替代方案
  • 箭头函数:继承外层 this,可替代部分 bind 场景:
    setTimeout(() => this.handleClick(), 1000); // 无需 bind
    
3. 实现简易版 bind
Function.prototype.myBind = function(context) {
  const self = this;
  const args = Array.prototype.slice.call(arguments, 1);
  return function() {
    return self.apply(context, args.concat(Array.prototype.slice.call(arguments)));
  };
};