前端JS: call、apply、bind 用法及实现

2 阅读2分钟

call、apply、bind 用法及实现

一、核心概念

三者都是 JavaScript 中函数的方法,用于改变函数执行时的 this 指向

二、基本用法对比

方法传参方式执行时机返回值
call参数列表立即执行函数执行结果
apply数组参数立即执行函数执行结果
bind参数列表返回新函数,可延迟执行绑定 this 的新函数

三、详细用法

1. call

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

const person = { name: 'Alice' };

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

2. apply

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

const numbers = [1, 2, 3];

// 用法
const result = sum.apply(null, numbers); // 输出: 6
console.log(result);

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

3. bind

function multiply(x, y) {
    return x * y;
}

const double = multiply.bind(null, 2); // 柯里化:固定第一个参数为2

console.log(double(5)); // 10
console.log(double(8)); // 16

四、手动实现

1. 实现 call

Function.prototype.myCall = function(context, ...args) {
    // 处理 context 为 null 或 undefined 的情况
    context = context || window || global;
    
    // 创建唯一键,避免属性覆盖
    const fnKey = Symbol('fn');
    
    // 将当前函数作为 context 的方法
    context[fnKey] = this;
    
    // 执行函数
    const result = context[fnKey](...args);
    
    // 删除临时添加的属性
    delete context[fnKey];
    
    return result;
};

// 测试
function testCall(a, b) {
    console.log(this.name, a + b);
    return a + b;
}

const obj = { name: 'Test' };
testCall.myCall(obj, 1, 2); // 输出: Test 3

2. 实现 apply

Function.prototype.myApply = function(context, argsArray) {
    context = context || window || global;
    const fnKey = Symbol('fn');
    
    context[fnKey] = this;
    
    // 处理参数,apply 接收数组参数
    const result = argsArray ? 
                  context[fnKey](...argsArray) : 
                  context[fnKey]();
    
    delete context[fnKey];
    return result;
};

// 测试
function testApply(a, b, c) {
    console.log(this.name, a + b + c);
    return a + b + c;
}

testApply.myApply(obj, [1, 2, 3]); // 输出: Test 6

3. 实现 bind

Function.prototype.myBind = function(context, ...bindArgs) {
    const self = this;
    
    // 返回的新函数
    const boundFunction = function(...callArgs) {
        // 判断是否通过 new 调用
        const isNewCall = this instanceof boundFunction;
        
        // 如果是 new 调用,this 指向新创建的对象
        // 否则使用绑定的 context
        const thisContext = isNewCall ? this : context;
        
        return self.apply(thisContext, [...bindArgs, ...callArgs]);
    };
    
    // 维护原型关系
    boundFunction.prototype = Object.create(self.prototype);
    
    return boundFunction;
};

// 测试
function Person(name, age) {
    this.name = name;
    this.age = age;
}

const BoundPerson = Person.myBind(null, 'John');
const p = new BoundPerson(25);
console.log(p.name, p.age); // 输出: John 25

五、实际应用场景

链接: juejin.cn/post/733978…

六、注意事项

  1. 严格模式下:如果 context 是 null 或 undefined,this 将指向 undefined
  2. 性能考虑:频繁使用 call/apply 可能影响性能
  3. 箭头函数:没有自己的 this,call/apply/bind 对箭头函数无效
  4. bind 多次绑定:多次 bind 只有第一次有效