返回

this 指向何方?从入门到彻底搞懂

前端

深入浅出详解 JavaScript 中的 this 关键词

在 JavaScript 这门神奇的语言中,this 关键词占据着举足轻重的地位。它就好比一个隐形的指南,时刻指引着程序执行的方向,确保代码的顺利运行。然而,对于初学者而言,this 的指向却往往令人捉摸不透,成为学习路上的一大障碍。本文将深入浅出地为你揭开 this 的神秘面纱,带你领略其在 JavaScript 代码中的魅力与奥秘。

this 的默认指向:对象或函数

通常情况下,this 指向当前执行代码所在的对象或函数。这意味着,当我们调用一个对象的方法时,this 指向该对象;当我们直接调用一个函数时,this 指向该函数本身。

const person = {
  name: "John Doe",
  greet: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is John Doe."

在上面的示例中,person.greet() 方法被调用时,this 指向 person 对象,因为该方法是作为 person 对象的一个属性被调用的。因此,this.name 等于 "John Doe"。

作用域和上下文对象

JavaScript 中的函数有两个重要的概念:作用域和上下文对象。作用域决定了变量的可见性,而上下文对象决定了 this 的指向。

函数的作用域由其代码块决定。 也就是说,在函数代码块内定义的变量只在该代码块内可见。

function greet() {
  const name = "John Doe";
  console.log(`Hello, my name is ${name}.`);
}

greet(); // Output: "Hello, my name is John Doe."

在上面的示例中,greet() 函数的作用域是其代码块,因此变量 name 只在该代码块内可见。

函数的上下文对象由其调用方式决定。 也就是说,this 的指向取决于函数是如何被调用的。

const person = {
  name: "John Doe",
  greet: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is John Doe."

const greetFunction = person.greet;
greetFunction(); // Output: "Hello, my name is undefined."

在上面的示例中,当我们调用 person.greet() 时,this 指向 person 对象,因为该方法是作为 person 对象的一个属性被调用的。然而,当我们调用 greetFunction() 时,this 却指向了 undefined,因为此时 greet() 方法不再是作为 person 对象的一个属性被调用,而是作为一个独立的函数被调用。

箭头函数

箭头函数是 ES6 中引入的一种新的函数语法,它没有自己的 this,而是继承其父级作用域的 this。这意味着,箭头函数中的 this 指向其所在函数的上下文对象。

const person = {
  name: "John Doe",
  greet: () => {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is undefined."

在上面的示例中,person.greet() 是一个箭头函数,它没有自己的 this,因此继承了其父级作用域的 this,即 person 对象。然而,由于 greet() 方法在箭头函数中被重新定义,this 指向 undefined

绑定

绑定是一种修改函数 this 指向的方法。有两种常见的绑定方式:显式绑定和隐式绑定。

显式绑定使用 bind() 方法。 它的作用是将一个函数永久绑定到一个指定的上下文对象。

const person = {
  name: "John Doe",
  greet: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

const greetFunction = person.greet.bind(person);
greetFunction(); // Output: "Hello, my name is John Doe."

在上面的示例中,我们使用 bind() 方法将 greet() 方法显式绑定到 person 对象。这意味着,无论 greet() 方法如何被调用,this 都始终指向 person 对象。

隐式绑定使用箭头函数。 箭头函数总是继承其父级作用域的 this

const person = {
  name: "John Doe",
  greet: () => {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is John Doe."

在上面的示例中,person.greet() 是一个箭头函数,它没有自己的 this,因此继承了其父级作用域的 this,即 person 对象。

类中的 this 指向

在 JavaScript 中,类是一种特殊的函数。类中的方法默认绑定到类本身,因此 this 指向类本身。

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const person = new Person("John Doe");
person.greet(); // Output: "Hello, my name is John Doe."

在上面的示例中,Person 类有一个构造函数和一个 greet() 方法。greet() 方法默认绑定到 Person 类本身,因此 this 指向 Person 类。这意味着,this.name 等于 "John Doe"。

总结

this 是 JavaScript 中一个至关重要的概念,理解其指向对于编写高质量的代码至关重要。一般情况下,this 指向当前执行代码的对象或函数。函数的作用域由其代码块决定,函数的上下文对象由其调用方式决定。箭头函数没有自己的 this,而是继承其父级作用域的 this。绑定是一种修改函数 this 指向的方法。在 JavaScript 中,类中的方法默认绑定到类本身,因此 this 指向类本身。

常见问题解答

  1. this 可以重新赋值吗?

    • 可以,但是不建议这样做,因为它可能导致混乱和难以调试的代码。
  2. 为什么箭头函数没有自己的 this

    • 箭头函数被设计为一种简短简洁的语法,没有自己的 this,以避免意外改变 this 的指向。
  3. 什么时候应该使用显式绑定?

    • 当需要确保函数在特定上下文中执行时,例如在回调函数或事件处理程序中。
  4. 什么时候应该使用隐式绑定?

    • 当不需要显式控制 this 的指向时,例如在对象的方法中。
  5. 类中的 this 和对象中的 this 有什么区别?

    • 对象中的 this 指向对象本身,而类中的 this 指向类本身。