this深度解析 — JavaScript `this` 指向原理与实践
官方: The this keyword refers to the context where a piece of code, such as a function’s body, is supposed to run. Most typically, it is used in object methods, where this refers to the object that the method is attached to, thus allowing the same method to be reused on different objects.
The value of this in JavaScript depends on how a function is invoked (runtime binding), not how it is defined. When a regular function is invoked as a method of an object (obj.method()), this points to that object. When invoked as a standalone function (not attached to an object: func()), this typically refers to the global object (in non-strict mode) or undefined (in strict mode). The Function.prototype.bind() method can create a function whose this binding doesn’t change, and methods Function.prototype.apply() and Function.prototype.call() can also set the this value for a particular call.
Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined. This behavior makes arrow functions particularly useful for callbacks and preserving context. However, arrow functions do not have their own this binding. Therefore, their this value cannot be set by bind(), apply() or call() methods, nor does it point to the current object in object methods.
概要
this 是 JavaScript 中的一个关键概念,它表示函数执行时的执行上下文。不同于很多语言,JavaScript 中 this 的指向在函数调用时确定,而不是在定义时决定。理解 this 的规则能帮助你避免许多常见错误(尤其是在事件回调、类方法、异步回调中)。
快速结论(口诀)
调用者决定 this,箭头函数继承外层 this,new 创建新对象并作为 this,call/apply/bind 显式指定。优先级:bind(显式,上下文固定) > new > call/apply > 隐式(对象方法) > 默认(全局或 undefined)。
一、this 的五条基本规则(从高到低优先级)
-
显式绑定(
bind优先)const fn2 = fn.bind(obj)会返回一个永久绑定obj的新函数;之后无论如何调用,this都是obj。
-
构造函数调用(
new)- 使用
new Fn()时,this指向新创建的实例对象(除非构造函数显式返回对象)。
- 使用
-
显式调用(
call/apply)fn.call(obj, ...)或fn.apply(obj, args)立即调用函数并把this设为obj。
-
隐式绑定(方法调用)
obj.fn(),this指向调用点左侧的对象obj(也称为“调用者”)。注意:如果链式访问或临时值则可能丢失。
-
默认绑定
- 普通函数直接调用(非严格模式下
this为全局对象window;严格模式下为undefined)。
- 普通函数直接调用(非严格模式下
注:箭头函数不按照以上规则,它不绑定
this,而是词法继承外层作用域的this(即创建时上下文的this)。
二、优先级与典型例子
1) 隐式绑定(对象方法)
const obj = {
name: 'Alice',
say() {
console.log(this.name);
}
};
obj.say(); // 'Alice' —— this = obj
2) 默认绑定
function foo() {
console.log(this);
}
foo(); // 非严格模式:window;严格模式:undefined
3) 显式绑定(call / apply / bind)
function greet(g) {
console.log(g + ', ' + this.name);
}
const user = { name: 'Bob' };
greet.call(user, 'Hello'); // Hello, Bob
greet.apply(user, ['Hi']); // Hi, Bob
const bound = greet.bind(user);
bound('Hey'); // Hey, Bob
4) new 绑定
function Person(name) {
this.name = name;
}
const p = new Person('Tom');
console.log(p.name); // Tom
5) 箭头函数(词法 this)
const obj = {
name: 'Lily',
method: function() {
const arrow = () => {
console.log(this.name);
};
arrow(); // 'Lily',箭头函数继承 method 的 this
}
};
obj.method();
三、call / apply / bind 的深入对比(何时用哪个)
- call(obj, arg1, arg2, …):立即调用,参数逐个传。
- apply(obj, [arg1, arg2, …]):立即调用,参数以数组形式传。
- bind(obj, …presetArgs):不立即调用,返回一个新函数,永久绑定
this,可作事件回调、延迟调用用。
示例 — 事件绑定与回调场景
class Button {
constructor(name, el) {
this.name = name;
this.el = el;
// 如果不 bind,callback 的 this 指向的是 DOM 节点而不是实例
this.el.addEventListener('click', this.onClick.bind(this));
}
onClick() {
console.log(this.name);
}
}
四、事件回调里的 this(浏览器行为)
- 在浏览器环境,使用
addEventListener注册事件时,事件处理函数内部的this默认是event.currentTarget(即事件监听被附加的对象),除非你用bind、箭头函数或其他技巧改变它。 - 官方参考:MDN 对
addEventListener与this的说明(文末参考链接)。
五、常见陷阱与误区
-
方法作为回调丢失
thisconst obj = { name: 'A', fn() { console.log(this.name); } }; setTimeout(obj.fn, 100); // this 丢失,默认绑定或 undefined(严格模式) // 解决:setTimeout(obj.fn.bind(obj), 100) 或 setTimeout(() => obj.fn(), 100) -
链式访问导致
this丢失const x = { a: { fn() { console.log(this); } } }; const f = x.a.fn; f(); // 默认绑定,this 不是 x.a -
箭头函数不是万能解决方案
- 在类方法中使用箭头函数可以避免 bind,但会在每次实例创建时重新分配函数,影响内存(如果大量实例需注意)。
- 箭头函数也不能作为构造函数(不能用 new)。
-
bind 的参数优先级
bind固定this,即使随后用call/apply也无法改变(绑定优先级高)。
-
在 React 中,类组件需要绑定事件处理
- 常见做法:
this.method = this.method.bind(this)或使用类字段语法method = () => {}。
- 常见做法:
六、调试与检查 this
- 在 Chrome 控制台里,可以直接在函数内
console.log(this)。 - 或使用
console.log(this === expectedObj)来断言。 - 在事件回调中,优先检查
event.target(触发元素)与event.currentTarget(绑定目标)的区别:this等同于event.currentTarget(非委托场景)。
七、工程实战建议(Best Practices)
- 明确边界:组件/类内的方法避免直接传裸函数作为回调;使用
bind、箭头函数或包装函数。 - prefer explicit binding:在构造函数中用
this.method = this.method.bind(this),或在使用地方用fn.bind(this)。 - 使用箭头函数处理闭包中的 this 问题:在内部回调里使用箭头函数继承
this。 - 避免滥用全局 this:开启严格模式(
'use strict')可避免非预期的全局 this。 - 在类/组件中尽量使用类字段(class fields)语法:可以在声明时用箭头函数保持 this,但要权衡性能。
- 写单元测试时显式传入 this:测试回调时用
call/apply显式指定上下文,增加可读性。
八、TypeScript 下的 this 小提示
- TypeScript 支持在方法签名中声明
this的类型:method(this: MyClass, arg: number) {},可以在编译期检查this的使用。 - 推荐在类中使用
public method = () => {}或在 constructor 中绑定,避免this未按预期指向的问题。
九、参考资料
- MDN:
this- JavaScript | MDN Web Docs - MDN:
EventTarget.addEventListener() - ECMAScript 规范(语言层面)与 HTML/DOM 事件模型(浏览器行为)文档(WHATWG/W3C)
十、示例集合(可直接复制运行)
请在浏览器控制台或Node环境(注意严格模式差异)运行下列示例理解 this:
<button id="b1">btn</button>
<script>
// 隐式绑定
const o = { v: 1, show() { console.log(this.v); } };
o.show(); // 1
// 丢失绑定
const f = o.show;
f(); // undefined 或 window.v
// 立即绑定
const bound = o.show.bind(o);
setTimeout(bound, 0); // 1
// 箭头继承 this
const o2 = {
v: 2,
method() {
const arrow = () => console.log(this.v);
arrow(); // 2
}
};
o2.method();
// 事件中的 this
document.getElementById('b1').addEventListener('click', function (e) {
console.log(this === e.currentTarget); // true
});
</script>
十一、总结(再次强调)
- 理解
this的核心在于调用方式,不是函数定义的位置。 - 箭头函数、
bind、call、apply、new都会改变或固定this的行为,了解优先级能帮助你写出更可靠的代码。 - 工程上优先使用显式绑定或箭头函数以避免回调时
this丢失,同时注意性能与内存开销。
更多推荐


所有评论(0)