JS基础:this指向

120 阅读3分钟

在 JavaScript 中,this 的指向是动态的,取决于函数的调用方式。以下是几种常见情况下 this 的指向规则:

全局作用域

在 非严格模式下this 指向全局对象(浏览器中是 window,Node.js 中是 global)。
在 严格模式下this 为 undefined

// 非严格模式
console.log(this); // window(浏览器中)

// 严格模式
"use strict";
console.log(this); // undefined

普通函数调用

无论函数是在哪里定义的,直接调用函数时,this 指向 全局对象(非严格模式)或 undefined(严格模式)。

function sayHello() {
  console.log(this);
}

sayHello(); // window(非严格模式)或 undefined(严格模式)

对象方法调用

当函数作为对象的方法调用时,this 指向调用该方法的 对象本身

const obj = {
  name: "Object",
  sayName: function () {
    console.log(this.name);
  }
};

obj.sayName(); // 输出 "Object"

需要注意的是如果把方法赋值给一个变量,this 会变成全局对象或 undefined(取决于是否严格模式)。

const obj = {
  name: "Object",
  sayName: function () {
    console.log(this.name);
  }
};

const func = obj.sayName;
func(); // undefined(因为此时丢失了调用的上下文)

构造函数调用

使用 new 调用构造函数时,this 指向新创建的对象。

function Person(name) {
  this.name = name;
}

const p = new Person("Alice");
console.log(p.name); // 输出 "Alice"

箭头函数

箭头函数的 this 是在函数定义时确定的,继承自其定义时的外层作用域的 this,并且不会因为调用方式改变。

const obj = {
  name: "Object",
  sayName: () => {
    console.log(this.name);
  }
};

obj.sayName(); // undefined(箭头函数的 this 指向外层作用域,这里是全局)

比较常见的在事件监听器或回调中使用箭头函数,避免 this 指向问题。

function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;
    console.log(this.seconds); // 正确指向 Timer 的实例
  }, 1000);
}

new Timer();

显式绑定

使用 callapplybind 可以显式指定 this 的指向。
call 和 apply 两者都立即调用函数,区别在于参数传递方式:

  • call:逐个传递参数。
  • apply:以数组形式传递参数。
function greet(greeting, punctuation) {
  console.log(greeting + " " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.call(person, "Hello", "!"); // 输出 "Hello Alice!"
greet.apply(person, ["Hi", "."]); // 输出 "Hi Alice."

bind:返回一个绑定了 this 的新函数,不会立即调用。

const person = { name: "Alice" };
const greetBound = greet.bind(person, "Hey");
greetBound("!"); // 输出 "Hey Alice!"

事件监听器

在事件监听器中,this 默认指向触发事件的元素。

const button = document.querySelector("button");
button.addEventListener("click", function () {
  console.log(this); // 输出 <button> 元素
});

需要注意的是使用箭头函数会改变 this 的指向,使其指向外层作用域。

button.addEventListener("click", () => {
  console.log(this); // 输出 window(箭头函数继承外层作用域的 this)
});

类的方法

在类中,普通方法的 this 指向调用它的对象实例。

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

const person = new Person("Alice");
person.sayName(); // 输出 "Alice"

需要注意的是 如果将类方法作为回调函数使用,可能会丢失 this。解决方法:

  • 使用箭头函数。
  • 手动绑定 this
class Person {
  constructor(name) {
    this.name = name;
  }
  sayName = () => {
    console.log(this.name);
  };
}

const person = new Person("Alice");
const func = person.sayName;
func(); // 输出 "Alice"

总结规则

  • 默认规则:直接调用函数时,this 是全局对象或 undefined(严格模式)。
  • 对象方法:this 是调用方法的对象。
  • 构造函数:this 是新创建的对象。
  • 箭头函数:this 是定义时的外层作用域。
  • 显式绑定:用 callapplybind 指定 this
  • 事件监听器:this 是触发事件的元素,箭头函数除外。