隐式绑定:对象方法调用时 `this` 的指向

好的,各位老铁,各位屏幕前的观众老爷们,欢迎来到“JavaScript冷知识与骚操作”系列讲座!今天咱们要聊点稍微有点绕,但又无比重要的东西——隐式绑定!

隐式绑定:this的“芳心暗许”

各位有没有经历过暗恋?那种小心翼翼,眼神总是追随着TA,恨不得TA的一举一动都和自己相关。JavaScript的this,在隐式绑定里,就像个暗恋中的小伙子,默默地把自己的心(也就是this)指向调用它的对象。

简单来说,当一个函数被作为对象的方法调用时,this就会自动指向这个对象。这就叫做隐式绑定。

1. 什么是this

在深入隐式绑定之前,我们先来简单回顾一下thisthis是JavaScript中的一个关键字,它代表函数执行时的上下文,也就是“谁在调用我?”。this的值不是固定的,它取决于函数被调用的方式。

可以把this想象成一个指向当前执行环境的指针,就像电影里的特写镜头,聚焦在当前场景的关键人物身上。

2. 隐式绑定的“套路”

隐式绑定的套路很简单:

  • 函数必须是对象的方法: 也就是说,这个函数必须是对象的一个属性,属性值是一个函数。
  • 通过对象调用函数: 使用 object.method() 的方式来调用函数。

当满足这两个条件时,函数内部的this就会指向object

举个栗子 🌰

const person = {
  name: '张三',
  age: 30,
  greet: function() {
    console.log(`你好,我是${this.name},今年${this.age}岁。`);
  }
};

person.greet(); // 输出:你好,我是张三,今年30岁。

在这个例子中,greet函数是person对象的一个方法。当我们使用person.greet()调用它时,greet函数内部的this就指向了person对象。因此,this.namethis.age就分别指向了person.nameperson.age

再来个更生动的例子:

const car = {
  brand: '宝马',
  model: 'X5',
  startEngine: function() {
    console.log(`启动${this.brand} ${this.model}引擎,轰轰轰!`);
  },
  showInfo: function() {
    console.log(`这是一辆${this.brand} ${this.model},颜色是${this.color || '未知'}。`);
  }
};

car.startEngine(); // 输出:启动宝马 X5引擎,轰轰轰!
car.showInfo();    // 输出:这是一辆宝马 X5,颜色是未知。

car.color = '黑色';
car.showInfo();    // 输出:这是一辆宝马 X5,颜色是黑色。

在这个例子中,startEngineshowInfo都是car对象的方法。通过car.startEngine()car.showInfo()调用它们时,this都指向了car对象。

3. 隐式绑定的“优先级”

隐式绑定虽然常见,但它并不是优先级最高的绑定方式。在JavaScript中,this的绑定优先级从高到低依次是:

  1. 显式绑定 (Explicit Binding): 使用 call, apply, 或 bind 显式地指定 this 的值。
  2. new 绑定 (new Binding): 使用 new 关键字创建对象时,this 指向新创建的对象。
  3. 隐式绑定 (Implicit Binding): 函数作为对象的方法调用时,this 指向该对象。
  4. 默认绑定 (Default Binding): 在非严格模式下,this 指向全局对象(浏览器中是 window,Node.js 中是 global);在严格模式下,thisundefined

这意味着,如果一个函数同时满足隐式绑定和其他绑定方式的条件,那么优先级更高的绑定方式会覆盖隐式绑定。

例如:

const obj1 = {
  name: '对象1',
  foo: function() {
    console.log(this.name);
  }
};

const obj2 = {
  name: '对象2'
};

obj1.foo();                   // 输出:对象1  (隐式绑定)
obj1.foo.call(obj2);           // 输出:对象2  (显式绑定,覆盖隐式绑定)

const myFunc = obj1.foo.bind(obj2);
myFunc();                     // 输出:对象2  (显式绑定,覆盖隐式绑定)

在这个例子中,obj1.foo() 满足隐式绑定的条件,所以 this 指向 obj1。但是,当我们使用 callbind 显式地将 this 绑定到 obj2 时,隐式绑定就被覆盖了。

4. 隐式绑定的“坑”

隐式绑定虽然方便,但也有一些需要注意的“坑”,一不小心就会掉进去:

  • 丢失this 当你把一个对象的方法赋值给一个变量,然后直接调用这个变量时,this可能会丢失,变成全局对象(非严格模式)或 undefined(严格模式)。

    const person = {
      name: '李四',
      greet: function() {
        console.log(`你好,我是${this.name}。`);
      }
    };
    
    const greetFunc = person.greet;
    greetFunc(); // 输出:你好,我是undefined。(严格模式)  或者 "你好,我是[object Window]" (非严格模式)

    在这个例子中,greetFunc 只是一个普通的函数引用,它不再是 person 对象的方法。因此,当调用 greetFunc() 时,this 丢失了。

    解决方法:

    • 使用 bind 显式地绑定 this

      const greetFunc = person.greet.bind(person);
      greetFunc(); // 输出:你好,我是李四。
    • 使用箭头函数(箭头函数没有自己的 this,它会继承外层作用域的 this):

      const person = {
        name: '王五',
        greet: () => {
          console.log(`你好,我是${this.name}。`); // 这里this指向window,因为箭头函数定义在person对象中
        }
      };
      
      person.greet(); // 输出:你好,我是undefined。(严格模式)

      注意: 箭头函数如果定义在对象字面量中,其 this 依然指向外层作用域,通常是全局对象(浏览器中是 window,Node.js 中是 global)。 要想箭头函数中的 this 指向对象,需要在对象的方法中使用箭头函数:

      const person = {
          name: '赵六',
          greet: function() {
              const innerGreet = () => {
                  console.log(`你好,我是${this.name}。`); // 这里this指向person
              };
              innerGreet();
          }
      };
      
      person.greet(); // 输出:你好,我是赵六。
  • 回调函数: 在一些回调函数中,this 的指向可能会发生改变。例如,在事件监听器中,this 通常指向触发事件的 DOM 元素。

    const button = document.querySelector('button');
    const myObj = {
      name: '我的对象',
      handleClick: function() {
        console.log(`按钮被点击了,我是${this.name}。`);
      }
    };
    
    button.addEventListener('click', myObj.handleClick); // 点击按钮后,this 指向 button 元素

    在这个例子中,点击按钮后,handleClick 函数会被调用,但是 this 并没有指向 myObj 对象,而是指向了 button 元素。

    解决方法:

    • 使用 bind 显式地绑定 this

      button.addEventListener('click', myObj.handleClick.bind(myObj)); // 点击按钮后,this 指向 myObj 对象
    • 使用箭头函数:

      button.addEventListener('click', () => {
        myObj.handleClick(); // 点击按钮后,this 指向 myObj 对象
      });

5. 总结

隐式绑定是JavaScript中this绑定的一种常见方式,它让函数可以方便地访问调用它的对象的属性。但是,我们需要注意隐式绑定的优先级和可能出现的this丢失问题,并使用 bind 或箭头函数来解决这些问题。

表格总结:

绑定方式 描述 优先级 适用场景
显式绑定 使用 callapplybind 显式地指定 this 的值。 最高 需要精确控制 this 指向的任何场景。
new 绑定 使用 new 关键字创建对象时,this 指向新创建的对象。 较高 构造函数中使用。
隐式绑定 函数作为对象的方法调用时,this 指向该对象。 中等 对象方法中使用。
默认绑定 在非严格模式下,this 指向全局对象(浏览器中是 window,Node.js 中是 global);在严格模式下,thisundefined 最低 独立函数调用,且没有其他绑定方式。

记住: 理解 this 的绑定规则是掌握 JavaScript 的关键一步。 多写代码,多练习,你就能像庖丁解牛一样,轻松驾驭 this 啦!

最后,送给大家一句至理名言:

"代码虐我千百遍,我待代码如初恋。" (希望大家在学习编程的道路上,保持热情,永不放弃!)

好啦,今天的分享就到这里。 如果觉得有用,记得点赞、收藏、转发哦! 咱们下期再见! 👋

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注