7. JavaScript 中的 this 指向哪里?

60 阅读3分钟

JavaScript 中的 this 指向哪里?

this 是一个 非常重要 但又 容易混淆 的概念。

this 关键字指向 是根据 函数的调用方式 来确定的,而 不是 在函数定义时 就决定的。理解 this绑定规则 非常关键,因为它决定了 函数内部 this指向的 对象

1 this 的默认绑定

当我们在 函数内部直接使用 this 时,如果 没有明确的绑定方式(比如 callapplybind),this指向 通常取决于 执行环境

1.1 在全局作用域中

  • 非严格模式 下,this 会 指向 全局对象(在浏览器中是 window);
  • 严格模式 ('use strict') 下,this 会是 undefined
// 示例代码 1
console.log(this);  // 在浏览器中,输出 Window 对象

'use strict';
console.log(this);  // 输出 undefined

1.2 在函数中

普通函数内部this 也指向 全局对象(在浏览器中是 window),除非 该函数被作为 对象的方法 调用(见示例代码 3)。

// 示例代码 2
function showThis() {
    console.log(this);  // 在浏览器中,输出 window 对象
}
showThis();

2 this 的对象方法调用绑定

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

// 示例代码 3
const person = {
    name: "Alice",
    greet: function() {
        console.log(this.name);
    }
};

person.greet(); // 输出 Alice,`this` 指向 person 对象

在上面的代码中,greet 方法作为 person 对象的方法 调用,所以 this 指向 person

3 this 的构造函数绑定

当我们通过 new 关键字 创建 一个对象时,this 会指向 新创建的 对象。

// 示例代码 4
function Person(name) {
    this.name = name;
}

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

在这个例子中,this 在构造函数中 指向 新创建的对象 person1

4 thiscallapplybind 方法绑定

callapplybind 都是 JavaScript 提供的 显式绑定 this 的方法。

4.1 call 方法

functionName.call(thisArg, arg1, arg2, ...);

call 方法 立即调用 函数,并且 可以指定 this 的指向。

// 示例代码 5
// 定义一个函数
function introduce(city, country) {
  console.log(`Hello, my name is ${this.name}. I live in ${city}, ${country}.`);
}

// 创建一个对象
const person = {
  name: "Alice"
};

// 使用 call 传递多个参数
introduce.call(person, "Paris", "France"); // Hello, my name is Alice. I live in Paris, France.

call 方法的 第一个参数 是 要绑定到 this对象后面 可以 传递参数 给函数。call 方法可以传递 任意数量 的参数。

4.2 apply 方法

functionName.apply(thisArg, [arg1, arg2, ...]);

apply 方法和 call 类似,不同的是 它的 参数 是以 数组的形式 传入。

// 示例代码 6
// 定义一个函数
function introduce(city, country) {
  console.log(`Hello, my name is ${this.name}. I live in ${city}, ${country}.`);
}

// 创建一个对象
const person = {
  name: "Alice"
};

// 使用 apply 传递多个参数
introduce.apply(person, ["Paris", "France"]);
使用 apply 处理不定数量的参数

假设我们有一个函数,想要计算多个数值的和,apply 可以很方便地实现。

// 示例代码 7
// 定义一个函数来求和
function sumAll() {
  const total = Array.from(arguments).reduce((acc, num) => acc + num, 0);
  console.log(`The total sum is: ${total}`);
}

// 创建一个对象
const person = {
  name: "Charlie"
};

// 使用 apply 调用并传递多个参数
sumAll.apply(person, [1, 2, 3, 4, 5]); // The total sum is: 15

4.3 bind 方法

functionName.bind(thisArg, arg1, arg2, ...);

bind 方法 返回 一个 新的函数,该函数 绑定了 指定的 this 值,而 不会 立即执行

// 示例代码 8
function greet() {
    console.log(`Hello, ${this.name}`);
}

const person = { name: "Alice" };
const greetPerson = greet.bind(person);
greetPerson();  // 输出 Hello, Alice

bind 返回的是 一个 新函数,它 始终保持 指定的 this 值(即对象 person)。

bind 与预设参数

使用 bind预设 一些 参数,这些预设参数 会在 新函数 调用时 自动传递

// 示例代码 9
// 定义一个函数
function greet(city, country) {
  console.log(`Hello, my name is ${this.name}. I live in ${city}, ${country}.`);
}

// 创建一个对象
const person = {
  name: "Bob"
};

// 使用 bind 预设一个参数
const greetInParis = greet.bind(person, "Paris");

// 调用时只需要传入剩余的参数
greetInParis("France"); // Hello, my name is Bob. I live in Paris, France.
  • greetInParis 是通过 bind 创建的一个 新函数,this 被绑定到 person 对象,city 参数 被预设为 "Paris";
  • 当调用 greetInParis 时,只需要传入 剩余的参数 "France",city 自动使用预设 的 "Paris"。

5 箭头函数的 this 绑定

箭头函数 与 普通函数 的一个 关键区别 是:箭头函数 没有 自己的 this,它会 继承 外部作用域this

// 示例代码 10
const person = {
    name: "Alice",
    greet: function() {
        setTimeout(() => {
            console.log(`Hello, ${this.name}`);
        }, 1000);
    }
};

person.greet();  // 输出 Hello, Alice

在上面的例子中,setTimeout 中的 箭头函数 继承greet 方法的 this,而 greet 函数 包含在对象中,也就是 person 对象方法的 this 指向 对象本身(即 person 对象)。

如果是 普通函数this 将指向 setTimeout 的全局对象(window),导致无法访问 person.name

6 this 与事件处理函数

事件处理函数 中,this 通常指向 触发事件的 元素(即 事件的 目标对象)。

// 示例代码 11
const button = document.querySelector("button");
button.addEventListener("click", function() {
    console.log(this);  // 输出 button 元素
});

如果使用 箭头函数this 就 不会 指向 button,而是 继承自 外部作用域

// 示例代码 12
button.addEventListener("click", () => {
    console.log(this);  // 输出外部作用域的 `this`,通常是 `window`
});

this 指向的规则总结

全局环境普通函数调用对象方法调用构造函数调用bind, apply, bind箭头函数事件处理
全局对象全局对象(非严格模式)调用的对象新创建的对象显式传入的对象继承外部作用域的 this触发事件的元素
浏览器中是 windowundefined(严格模式)