深入理解 this 指向
🎯 引言
通过这篇文章,你将掌握 JavaScript 中非常核心但容易混淆的 this 指向规则。你会知道 this 是什么、什么时候指向哪个对象,以及如何通过 call 和 apply 改变 this。你还会理解定时器和回调函数中 this 为啥表现不同,以及箭头函数是如何“偷走”this 的。掌握这些知识后,写代码时就不会因为 this 指向错误而出错,调试也能轻松很多。
🔍 什么是 this?它指向什么?
在 JavaScript 里,this 是一个特殊的关键字,它代表当前执行上下文中的“拥有者”。简单来说,this 指向的是当前执行代码所在的环境对象。
举个生活例子:
你在家里做饭,这时候“我”是“我自己”,你是主人;换到朋友家做饭,“我”仍然是我,但这时候“家”的主人变了,厨房的规则也不同。
this 也是类似的:它的指向取决于函数调用的方式和上下文环境。
🔥 函数直接调用时的 this
当我们直接调用一个普通函数时,比如:
function show() {
console.log(this);
}
show();
这时候,this 指向什么呢?
- 在浏览器的非严格模式下,this 会指向全局对象,也就是
window。 - 在严格模式下,this 是
undefined。
为什么?因为函数没有被任何对象调用,默认就指向全局(或严格模式下为 undefined),show() 是省略后的写法,实际上是 window.show()
🐻 作为对象方法调用的 this
当函数作为对象的方法调用时,this 就指向调用它的对象。
看这个例子:
const person = {
name: '小明',
sayHi: function () {
console.log('Hi, 我是', this.name);
},
};
person.sayHi();
调用 person.sayHi() 时,this 指向 person 对象,所以输出是:
Hi, 我是 小明
生活例子:
哇,这个房子装修得真漂亮!如果你在自己家说这句话,它指的就是你家;在朋友家说,就指的是朋友家。
⏰ 定时器里的 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 提供了 call 和 apply 两个方法,能够帮我们手动指定函数执行时 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, '北京'])
🚀 箭头函数中的 this
箭头函数是 ES6 的课程,后续在回顾这部分。
箭头函数对 this 的处理和普通函数不一样。它根本没有自己的 this,this 是“继承”自外层函数的 this。
举个例子:
const obj = {
name: '小强',
greet: function () {
const arrow = () => {
console.log(this.name);
};
arrow();
},
};
obj.greet(); // 输出:小强
箭头函数的 this 继承了外层的 this,也就是 obj。
生活比喻:
箭头函数像跟班,跟着主人(外层函数)的脚步走,不会自己变成别人。
🧾 小节总结
- 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。
🧪 小练习
- 修改下面代码,让 setTimeout 中输出的 this 指向 person 对象。提示:运用箭头函数。
const person = {
name: '小白',
sayHi: function () {
setTimeout(function () {
console.log(this.name);
}, 500);
},
};
person.sayHi();
- 使用 call 或 apply 调用函数 greet,使得 this 指向下面的对象。
function greet() {
console.log('你好,' + this.name);
}
const user = { name: '小红' };
// 在这里调用 greet,使 this 指向 user
🎉 恭喜你已经掌握 this 的基本指向规则、call/apply 和箭头函数的 this 继承机制啦!继续练习,你会写出更棒的 JavaScript 代码!
