1. 基本概念

这三个方法都是用来改变函数执行时的this指向,但它们在执行时机、参数传递和返回值方面有重要区别。

2. 核心区别对比

特性call()apply()bind()
执行时机立即执行立即执行返回新函数,延迟执行
参数传递参数列表参数数组参数列表
返回值函数执行结果函数执行结果新函数
this绑定临时绑定临时绑定永久绑定

3. 详细语法对比

3.1 call() 方法

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

3.2 apply() 方法

function.apply(thisArg, [arg1, arg2, ...])

3.3 bind() 方法

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

4. 代码示例对比

4.1 基本使用示例

const person = {
    name: '张三',
    age: 25,
    sayHello: function(greeting, punctuation) {
        console.log(`${greeting},我是${this.name}${punctuation}`);
    }
};
 
const anotherPerson = {
    name: '李四',
    age: 30
};
 
// call() 方法
person.sayHello.call(anotherPerson, '你好', '!');
// 输出:你好,我是李四!
 
// apply() 方法
person.sayHello.apply(anotherPerson, ['你好', '!']);
// 输出:你好,我是李四!
 
// bind() 方法
const boundSayHello = person.sayHello.bind(anotherPerson, '你好', '!');
boundSayHello(); // 输出:你好,我是李四!

4.2 参数传递对比

function sum(a, b, c) {
    return a + b + c;
}
 
const numbers = [1, 2, 3];
 
// call() - 需要展开数组
console.log(sum.call(null, ...numbers)); // 6
 
// apply() - 直接传入数组
console.log(sum.apply(null, numbers)); // 6
 
// bind() - 可以部分传参
const boundSum = sum.bind(null, 1, 2);
console.log(boundSum(3)); // 6

6. 手写实现

6.1 手写call方法

Function.prototype.myCall = function(context, ...args) {
    // 如果context为null或undefined,则指向全局对象
    context = context || window;
    
    // 将函数作为context的一个属性
    context.fn = this;
    
    // 执行函数
    const result = context.fn(...args);
    
    // 删除临时属性
    delete context.fn;
    
    return result;
};
 
// 测试
const obj = { name: '测试' };
function test(a, b) {
    console.log(this.name, a, b);
}
 
test.myCall(obj, 1, 2); // 测试 1 2

6.2 手写apply方法

Function.prototype.myApply = function(context, args = []) {
    context = context || window;
    context.fn = this;
    
    const result = context.fn(...args);
    delete context.fn;
    
    return result;
};
 
// 测试
test.myApply(obj, [1, 2]); // 测试 1 2

6.3 手写bind方法

Function.prototype.myBind = function(context, ...args1) {
    const fn = this;
    
    return function(...args2) {
        return fn.apply(context, [...args1, ...args2]);
    };
};
 
// 测试
const boundTest = test.myBind(obj, 1);
boundTest(2); // 测试 1 2

8. 常见面试题

8.1 经典题目

var name = '全局';
var obj = {
    name: '对象',
    getName: function() {
        return this.name;
    }
};
 
var getName = obj.getName;
 
console.log(obj.getName()); // '对象'
console.log(getName()); // '全局'
console.log(getName.call(obj)); // '对象'
console.log(getName.apply(obj)); // '对象'
console.log(getName.bind(obj)()); // '对象'