源本科技 | 码上会

JavaScript 函数绑定

2025/12/30
34
0

学习目标

  • 理解 JavaScript 中 this 的上下文机制

  • 掌握 bind()call()apply() 三种函数绑定方法的使用场景与区别

  • 了解箭头函数对 this 绑定的特殊处理方式

  • 能够在实际开发中正确控制函数执行上下文


什么是函数绑定

在 JavaScript 中,函数绑定 是指将一个函数与其特定的执行上下文(即 this 值)关联起来的过程。

关键概念:

  • this 绑定:JavaScript 函数总是在某个上下文中执行。默认情况下,在非严格模式下 this 指向全局对象(如浏览器中的 window),在严格模式下则为 undefined

  • 永久绑定:使用 bind() 方法绑定后的函数,无论以何种方式调用,其 this 始终指向绑定时指定的对象。

  • 部分应用(Partial Application)bind() 还支持预设部分参数,生成一个新的“部分应用”函数。

示例:未绑定导致的问题

const person = {
    name: 'Coder',
    greet: function() {
        console.log('Hello, ' + this.name);
    }
};

const greet = person.greet;
greet(); // 输出:Hello, undefined

原因分析:当 greet 被赋值给变量后独立调用时,它脱离了 person 对象的上下文,this 不再指向 person,因此 this.nameundefined(严格模式下)或全局对象的 name(非严格模式下,通常为空)。


三种主要方法

1. bind()

创建绑定后的新函数

bind() 不会立即执行函数,而是返回一个新函数,该函数的 this 被永久绑定到指定对象。

const person = {
    name: 'Coder',
    greet: function() {
        console.log('Hello, ' + this.name);
    }
};

const greet = person.greet;
const boundGreet = greet.bind(person);
boundGreet(); // 输出:Hello, Coder

适用场景:事件处理器、回调函数、需要延迟执行但保持上下文的场合。


2. call()

立即调用并指定 this

call() 立即执行函数,第一个参数为 this 的值,后续参数依次传入。

const person = {
    name: 'Coder',
    greet: function(city) {
        console.log('Hello, ' + this.name + ' from ' + city);
    }
};

person.greet('Beijing');           // 直接调用
const greet = person.greet;
greet.call(person, 'Shanghai');   // 使用 call 绑定 this 并传参

输出:

Hello, Coder from Beijing
Hello, Coder from Shanghai

适用场景:临时借用其他对象的方法,或需要动态设置上下文并立即执行。


3. apply()

类似 call(),但参数以数组形式传入

apply()call() 功能相同,区别仅在于参数传递方式apply 接收一个参数数组。

const person = {
    name: 'Coder',
    greet: function(city, country) {
        console.log('Hello, ' + this.name + ' from ' + city + ', ' + country);
    }
};

person.greet('Beijing', 'China');
const greet = person.greet;
greet.apply(person, ['Shenzhen', 'Guangdong']);

输出:

Hello, Coder from Beijing, China
Hello, Coder from Shenzhen, Guangdong

适用场景:当你已经有一个参数数组(如 argumentsArray.from(...) 的结果)时,使用 apply 更方便。


箭头函数与 this 绑定

箭头函数没有自己的 this

它会继承外层作用域的 this(词法作用域),这使得在某些场景下无需手动绑定。

const person = {
    name: 'Coder',
    greet: function() {
        const arrowGreet = () => {
            console.log('Hello, ' + this.name); 
        };
        arrowGreet();
    }
};

person.greet(); // 输出:Hello, Coder

原理说明arrowGreet 是箭头函数,它的 this 来自外层 greet 方法的上下文。而 greet 是作为 person.greet() 调用的,因此 this 指向 person

注意:正因如此,不要在对象方法中使用箭头函数作为主方法,否则 this 会指向定义时的外部作用域(通常是全局),而非对象本身。

// ❌ 错误示例
const badPerson = {
    name: 'Coder',
    greet: () => {
        console.log('Hello, ' + this.name); // this 指向全局,name 为 undefined
    }
};
badPerson.greet(); // Hello, undefined

三种方法对比

方法

是否立即执行

参数传递方式

主要用途

bind()

从第二个参数开始逐个传入

创建绑定上下文的新函数(常用于回调)

call()

从第二个参数开始逐个传入

立即调用并指定 this

apply()

第二个参数为数组

立即调用,且参数已存在于数组中


重点总结

  1. this 的值取决于函数如何被调用,而非如何定义。

  2. bind() 返回新函数,不执行;call()/apply() 立即执行。

  3. 箭头函数没有自己的 this,继承自外层作用域。

  4. 在类方法、事件监听器、定时器回调中,常需使用 bind() 防止 this 丢失。

  5. apply 特别适合配合 Math.maxArray.prototype.push 等接受多个参数的函数使用。


思考题

  1. 为什么在 React 类组件中,事件处理函数通常需要在构造函数中使用 this.handleClick = this.handleClick.bind(this)

  2. 如果你有一个函数 fn(a, b, c),现在有一个数组 args = [1, 2, 3],如何用 apply 调用它?如果改用 bind,又该如何实现延迟调用?

  3. 在以下代码中,arrowFunc 中的 this 指向谁?为什么?

const obj = {
    value: 42,
    method: function() {
        setTimeout(() => {
            console.log(this.value);
        }, 100);
    }
};
obj.method();