call() 、bind()、 apply() 有哪些区别

5 阅读3分钟

call()、apply()、bind() 都是 JavaScript 中用于显式绑定函数执行上下文(this 值)  的方法,但它们在参数传递和调用时机上有重要区别。

📌 核心区别总结

方法调用时机参数形式返回值
call()立即执行参数列表(逐个传递)函数的执行结果
apply()立即执行参数数组(单个数组)函数的执行结果
bind()不立即执行,返回新函数参数列表(可部分传入)返回一个新函数(绑定函数)

🔍 详细解析

1. call()

func.call(thisArg, arg1, arg2, ...)

特点:

  • 立即调用函数
  • 参数以逗号分隔的列表传递
  • 适合参数个数固定的情况

示例:

function introduce(greeting, punctuation) {
  console.log(`${greeting}, 我是${this.name}${punctuation}`);
}

const person = { name: '张三' };

introduce.call(person, '你好', '!');
// 输出:你好, 我是张三!

2. apply()

func.apply(thisArg, [argsArray])

特点:

  • 立即调用函数
  • 参数以数组形式传递(或类数组对象)
  • 适合参数个数不确定,或已有数组的情况

示例:

function introduce(greeting, punctuation) {
  console.log(`${greeting}, 我是${this.name}${punctuation}`);
}

const person = { name: '李四' };

introduce.apply(person, ['Hello', '!!!']);
// 输出:Hello, 我是李四!!!

// 实际应用:数组求最大值
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers); // 7

3. bind()

func.bind(thisArg, arg1, arg2, ...)

特点:

  • 不立即调用,而是返回一个新函数(绑定函数)
  • 返回的函数可多次调用,且每次 this 都指向绑定的值
  • 支持参数预设(柯里化)

示例:

function introduce(greeting, punctuation) {
  console.log(`${greeting}, 我是${this.name}${punctuation}`);
}

const person = { name: '王五' };

// 创建绑定函数,预设部分参数
const boundFunc = introduce.bind(person, 'Hi');
boundFunc('!!!');      // 输出:Hi, 我是王五!!!
boundFunc('?');        // 输出:Hi, 我是王五?

// 实际应用:事件处理函数
const button = document.querySelector('button');
button.addEventListener('click', function() {
  console.log(this.textContent); // this 指向 button
}.bind(button));

🎯 关键对比

参数传递方式

function sum(a, b, c) {
  return a + b + c;
}

const obj = {};

// call:逐个传递参数
sum.call(obj, 1, 2, 3);    // 6

// apply:数组传递
sum.apply(obj, [1, 2, 3]); // 6

// bind:参数预设 + 后续传递
const boundSum = sum.bind(obj, 1);
boundSum(2, 3);            // 6
boundSum(4, 5);            // 10 (1 + 4 + 5)

执行时机

const obj = { value: 10 };

function show() {
  console.log(this.value);
}

// call 和 apply 立即执行
show.call(obj);    // 立即输出:10

// bind 返回函数,稍后执行
const boundShow = show.bind(obj);
boundShow();       // 稍后调用,输出:10

💡 实际应用场景

1. 借用方法

// 借用数组方法处理类数组对象
function sumArguments() {
  // arguments 是类数组,没有 reduce 方法
  return Array.prototype.reduce.call(
    arguments, 
    (acc, curr) => acc + curr, 
    0
  );
}
sumArguments(1, 2, 3, 4); // 10

2. 构造函数继承

function Parent(name) {
  this.name = name;
}

function Child(name, age) {
  // 借用父类构造函数
  Parent.call(this, name);
  this.age = age;
}

3. 柯里化(部分应用函数)

function multiply(a, b, c) {
  return a * b * c;
}

// 预设前两个参数
const double = multiply.bind(null, 2, 3);
double(4); // 24 (2 * 3 * 4)
double(5); // 30 (2 * 3 * 5)

4. React/Vue 中的事件处理

// React 类组件中
class Button extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    console.log(this.props); // this 正确指向组件实例
  }
  
  render() {
    return <button onClick={this.handleClick}>点击</button>;
  }
}

⚠️ 注意事项

  1. 箭头函数无自己的 this
const func = () => console.log(this);
func.call({value: 1}); // this 不会改变,指向定义时的上下文
  1. 参数为 null/undefined
// 非严格模式下,null/undefined 会被替换为全局对象(浏览器中为 window)
// 严格模式下,保持为 null/undefined
function test() { console.log(this); }
test.call(null); // 非严格模式:window,严格模式:null
  1. 性能考虑
    • bind() 每次返回新函数,频繁使用可能产生大量函数对象
    • 缓存绑定函数可优化性能

🆚 现代替代方案

展开运算符替代 apply

// 以前
Math.max.apply(null, [1, 2, 3]);

// 现在
Math.max(...[1, 2, 3]);  // 展开运算符

箭头函数避免 this 绑定

// 以前需要 bind
const obj = {
  value: 10,
  getValue: function() {
    return this.value;
  }.bind(this)
};

// 现在可用箭头函数
const obj = {
  value: 10,
  getValue: () => this.value  // this 自动绑定到外层
};

📝 总结口诀

  • call:立即调用,参数一个一个
  • apply:立即调用,参数数组一起传
  • bind:不调用,返回新函数,可预设参数

理解这三个方法对于掌握 JavaScript 的 this 机制和函数式编程至关重要。在实际开发中,根据需求灵活选择合适的方法。