JavaScript 原理

深入理解 this 指向

本文帮你系统掌握 JavaScript 中 this 的指向规则,带你理解函数调用、对象方法、定时器、call/apply 以及箭头函数中的 this,适合前端入门小白。

🎯 引言

通过这篇文章,你将掌握 JavaScript 中非常核心但容易混淆的 this 指向规则。你会知道 this 是什么、什么时候指向哪个对象,以及如何通过 call 和 apply 改变 this。你还会理解定时器和回调函数中 this 为啥表现不同,以及箭头函数是如何“偷走”this 的。掌握这些知识后,写代码时就不会因为 this 指向错误而出错,调试也能轻松很多。


🔍 什么是 this?它指向什么?

在 JavaScript 里,this 是一个特殊的关键字,它代表当前执行上下文中的“拥有者”。简单来说,this 指向的是当前执行代码所在的环境对象

举个生活例子:

你在家里做饭,这时候“我”是“我自己”,你是主人;换到朋友家做饭,“我”仍然是我,但这时候“家”的主人变了,厨房的规则也不同。

this 也是类似的:它的指向取决于函数调用的方式和上下文环境。

this 不是固定的,它的值会随着调用环境改变。理解 this,关键是弄清楚“谁调用了函数”。

🔥 函数直接调用时的 this

当我们直接调用一个普通函数时,比如:

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

这时候,this 指向什么呢?

  • 在浏览器的非严格模式下,this 会指向全局对象,也就是 window
  • 在严格模式下,this 是 undefined

为什么?因为函数没有被任何对象调用,默认就指向全局(或严格模式下为 undefined),show() 是省略后的写法,实际上是 window.show()

很多初学者遇到 this 打印出来是 window 或者 undefined,容易迷惑。记住,函数直接调用,this 就是全局或 undefined。

🐻 作为对象方法调用的 this

当函数作为对象的方法调用时,this 就指向调用它的对象。

看这个例子:

const person = {
    name: '小明',
    sayHi: function () {
        console.log('Hi, 我是', this.name);
    },
};
person.sayHi();

调用 person.sayHi() 时,this 指向 person 对象,所以输出是:

Hi, 我是 小明

生活例子:

哇,这个房子装修得真漂亮!如果你在自己家说这句话,它指的就是你家;在朋友家说,就指的是朋友家。

方法调用时,this 总是指向调用它的那个“对象”。

⏰ 定时器里的 this、回调里的 this(比如 setTimeout)

在定时器和回调中,this 往往很容易让人迷糊:

const obj = {
    name: '小红',
    greet: function () {
        setTimeout(function () {
            console.log(this.name);
        }, 1000);
    },
};
obj.greet();

运行结果是:undefined

为什么?因为 setTimeout 里的回调函数是普通函数调用,this 指向全局对象 window(或者严格模式下 undefined)。它和 obj 没有关系。

如果想让 this 指向 obj,就需要特殊处理。


✂️ 如何改变 this?—— call/apply 简单讲解

JavaScript 提供了 callapply 两个方法,能够帮我们手动指定函数执行时 this 的指向。

function sayHello() {
    console.log('Hello, 我是', this.name);
}

const user = { name: '小张' };
sayHello.call(user); // Hello, 我是 小张
sayHello.apply(user); // Hello, 我是 小张

区别:

  • call 传参是逗号分隔的,例如 call(user, 25, '北京')
  • apply 传参是数组形式。例如 call(user, [25, '北京'])
call 和 apply 可以让普通函数“变身”成对象的方法,让 this 指向我们指定的对象。

🚀 箭头函数中的 this

箭头函数是 ES6 的课程,后续在回顾这部分。

箭头函数对 this 的处理和普通函数不一样。它根本没有自己的 this,this 是“继承”自外层函数的 this

举个例子:

const obj = {
    name: '小强',
    greet: function () {
        const arrow = () => {
            console.log(this.name);
        };
        arrow();
    },
};
obj.greet(); // 输出:小强

箭头函数的 this 继承了外层的 this,也就是 obj。

生活比喻:

箭头函数像跟班,跟着主人(外层函数)的脚步走,不会自己变成别人。

箭头函数不能用作构造函数,也不能用 call/apply 改变它的 this。

🧾 小节总结

  • this 是函数执行时的上下文对象,它的指向随调用环境变化。
  • 普通函数直接调用时,this 指向全局或 undefined。
  • 作为对象方法调用时,this 指向调用它的对象。
  • setTimeout 和回调中普通函数里的 this 指向全局,需要用 call/apply 或箭头函数来解决。
  • call 和 apply 可以手动改变函数执行时的 this。
  • 箭头函数没有自己的 this,继承自外层函数。

❓ 知识问答(Q&A)

Q:普通函数和箭头函数中的 this 有啥区别?
A:普通函数里的 this 是调用时决定的,箭头函数的 this 是定义时继承外层的 this。

Q:setTimeout 里的回调函数为什么不能直接用对象的 this?
A:因为回调函数是普通函数调用,this 指向全局,需要特殊手法才能指向对象。

Q:call 和 apply 有什么区别?
A:call 传参用逗号分隔,apply 传参用数组,两者都能改变 this。


🧪 小练习

  1. 修改下面代码,让 setTimeout 中输出的 this 指向 person 对象。提示:运用箭头函数。
const person = {
    name: '小白',
    sayHi: function () {
        setTimeout(function () {
            console.log(this.name);
        }, 500);
    },
};
person.sayHi();
  1. 使用 call 或 apply 调用函数 greet,使得 this 指向下面的对象。
function greet() {
    console.log('你好,' + this.name);
}

const user = { name: '小红' };

// 在这里调用 greet,使 this 指向 user

🎉 恭喜你已经掌握 this 的基本指向规则、call/apply 和箭头函数的 this 继承机制啦!继续练习,你会写出更棒的 JavaScript 代码!