JavaScript 中 this 指向详解(含多种绑定形式与陷阱)
this
是 JavaScript 中用于函数执行上下文的特殊关键字,表示函数运行时的所属对象。它的指向取决于函数的调用方式。
🧠 为什么存在 this
- 提供统一的上下文访问机制。
- 支持对象方法复用。
- 便于类/构造函数共享行为。
🧩 一、四种 this 绑定规则详解
1️⃣ 默认绑定(Direct Function Call)
function sayHi() {
console.log(this);
}
sayHi(); // 非严格模式:window;严格模式:undefined
- 非严格模式:
this
指向全局对象window
- 严格模式:
this
为undefined
2️⃣ 隐式绑定(Object Method Call)
const person = {
name: 'Tom',
greet() {
console.log(this.name);
}
};
person.greet(); // 输出 Tom
- 函数通过对象调用时,
this
指向该对象。
⚠️ 隐式绑定丢失示例:
const greetFn = person.greet;
greetFn(); // undefined 或 window 上的 name
- 函数独立引用后,脱离了原对象,变为默认绑定。
3️⃣ 显式绑定(call
、apply
、bind
)
function introduce(lang) {
console.log(`${lang}: I'm ${this.name}`);
}
const user = { name: 'Alice' };
introduce.call(user, 'EN');
introduce.apply(user, ['FR']);
const boundFn = introduce.bind(user, 'CN');
boundFn();
方法 | 是否立即执行 | 参数传递方式 |
---|---|---|
call |
✅ | 逗号分隔参数 |
apply |
✅ | 数组形式参数 |
bind |
❌ | 返回绑定后的新函数 |
4️⃣ new 绑定(构造函数)
function Person(name) {
this.name = name;
}
const p = new Person('Eve');
console.log(p.name); // Eve
等价执行流程:
function Person(name) {
// this = {}
this.name = name;
// return this;
}
🔢 二、this 绑定优先级(从高到低)
new 绑定 > 显式绑定(bind/call/apply)> 隐式绑定 > 默认绑定
function showName() {
console.log(this.name);
}
const obj = { name: 'Leo' };
const bound = showName.bind(obj);
const n = new bound(); // this 指向新创建的对象,而非 obj
🚀 三、箭头函数中的 this
const obj = {
name: 'Arrow',
say() {
const arrowFn = () => {
console.log(this.name);
};
arrowFn();
}
};
obj.say(); // 输出:Arrow
箭头函数不会创建自己的 this
:
const person = {
name: 'Mike',
greet: () => {
console.log(this.name);
}
};
person.greet(); // undefined
this
取决于定义时的上下文,不是调用者。
🔍 四、this 在不同场景下的指向
✅ 事件监听器中
document.querySelector('#btn').addEventListener('click', function () {
console.log(this); // 触发事件的元素
});
document.querySelector('#btn').addEventListener('click', () => {
console.log(this); // 外层作用域(通常为 window)
});
✅ setTimeout/setInterval
setTimeout(function () {
console.log(this); // window
}, 1000);
✅ class 方法
class Person {
constructor(name) {
this.name = name;
this.sayHi = this.sayHi.bind(this);
}
sayHi() {
console.log(this.name);
}
}
或使用箭头函数:
class Person {
name = 'Anna';
sayHi = () => {
console.log(this.name);
}
}
⚠️ 五、常见陷阱与解决方案
this 丢失:异步/回调场景
const obj = {
name: 'Obj',
run() {
setTimeout(this.say, 1000); // ❌ 默认绑定
},
say() {
console.log(this.name);
}
};
obj.run(); // undefined
✅ 解决方案:
setTimeout(this.say.bind(this), 1000);
// 或
setTimeout(() => this.say(), 1000);
✅ this 绑定规则总结表
调用方式 | this 指向 |
---|---|
普通函数 | 全局对象(非严格);undefined(严格) |
对象方法 | 该对象 |
箭头函数 | 定义时的外层作用域 |
构造函数(new) | 新创建的实例对象 |
call/apply/bind | 显式传入的对象 |
事件监听器(function) | 触发事件的 DOM 元素 |
事件监听器(箭头) | 外层作用域 |
setTimeout/setInterval | 全局对象 |
class 方法 | 调用者(需手动绑定) |
📚 建议与技巧
- 在回调中优先使用箭头函数,避免
this
丢失。 - 使用
.bind(this)
或箭头函数保证类方法中this
正确。 - 避免函数“脱离对象”后独立调用。
- 善用箭头函数与显式绑定,减少 this 混乱。