JavaScript 中的 this 关键字常常让人困惑,因为它的值不是在函数定义时确定的,而是在函数被调用时根据调用方式动态绑定。换句话说,同一个函数在不同地方调用,内部的 this 可能完全不同。理解这种动态性是掌握 this 指向的第一步。

很多开发者从面向对象语言转到 JavaScript 时,会习惯性地认为 this 指向当前对象或类实例。这个直觉在对象方法调用时成立,但在全局函数、回调函数、定时器等场景中却会失效。要准确判断 this,关键不是看函数写在哪里,而是看函数如何被调用。
一、调用位置决定 this:先找到调用栈
JavaScript 函数执行的瞬间会形成一个调用位置,也就是函数在哪里被调用。分析 this 的第一步是找到这个调用位置,它通常不是函数声明的位置,而是函数名前面那个调用表达式所在的位置。
例如,同样一个函数 showName 被赋给 obj 对象后,通过 obj.showName() 调用时,调用位置就是 obj.showName;而直接 showName() 调用时,调用位置是全局。前者把 this 绑定到 obj,后者在非严格模式下绑定到全局对象。严格模式下直接调用则为 undefined,因为默认绑定不再回退到全局对象。
function showName() {
console.log(this.name);
}
var name = '全局';
var obj = {
name: '局部',
showName: showName
};
showName(); // 全局
obj.showName(); // 局部
从代码输出可以看到,函数体完全一样,但调用方式不同导致 this 不同。理解调用位置后,再看四种绑定规则会更加清晰。
二、四种绑定规则:默认、隐式、显式与 new
默认绑定是最基础的规则,当函数直接调用且不属于任何对象时触发。在非严格模式下,独立函数调用中的 this 指向全局对象;在严格模式下则为 undefined。严格模式的影响还取决于函数体是否处于严格模式,如果调用位置在严格模式但函数体不在,仍然遵循非严格模式的默认绑定,需要特别留意。
隐式绑定发生在函数作为对象方法被调用时。此时函数调用位置前的对象就是 this。常见的错误是方法被赋值给变量后再调用,隐式绑定会丢失,this 退回到默认绑定。这种隐式丢失在回调传参中尤其常见,例如 setTimeout(obj.method, 100) 实际执行的是独立函数调用。
var user = {
name: 'Bob',
sayName: function() {
console.log(this.name);
}
};
var fn = user.sayName;
fn(); // 非严格模式下输出 undefined 或全局 name,而不是 Bob
显式绑定通过 call、apply 和 bind 方法直接指定 this。call 与 apply 立即执行函数,区别在于参数传递方式:call 逐个传参,apply 使用数组传参。bind 返回一个绑定了 this 的新函数,不会立即执行,适合用在回调场景中固定 this。
function introduce(city, country) {
console.log(this.name + ' 来自 ' + city + ', ' + country);
}
var person = { name: 'Carol' };
introduce.call(person, '上海', '中国');
introduce.apply(person, ['北京', '中国']);
var boundIntroduce = introduce.bind(person, '广州');
boundIntroduce('中国'); // Carol 来自 广州, 中国
new 绑定则发生在使用 new 关键字调用构造函数时。此时 JavaScript 会创建一个新对象,并把构造函数内部的 this 指向这个新对象。除非构造函数显式返回一个对象,否则 new 调用最终返回的就是这个新对象。
function Product(title) {
this.title = title;
}
var phone = new Product('手机');
console.log(phone.title); // 手机
四种规则的优先级从高到低依次为:new 绑定、显式绑定、隐式绑定、默认绑定。这意味着显式绑定可以覆盖隐式绑定,但无法覆盖 new 绑定;new 与显式绑定同时使用时,bind 传入的 this 会被 new 创建的新对象替代。
三、箭头函数:从外层作用域继承 this
箭头函数没有自己的 this,它的 this 在定义时从外层最近的普通函数或全局作用域中捕获。正因如此,箭头函数内部的 this 不会因为调用方式不同而改变,call、apply、bind 也无法修改它。这个特性让箭头函数非常适合用于回调,避免传统函数中 this 丢失的问题。
function Counter() {
this.count = 0;
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
new Counter();
相反,如果对象方法需要使用对象自身的属性,就不要用箭头函数定义方法,否则 this 会捕获外层作用域而非方法所属对象。例如在对象字面量中直接写箭头函数方法,this 可能指向全局对象或外层实例,导致读取属性失败。
var obj = {
value: 10,
getValue: () => {
return this.value;
}
};
console.log(obj.getValue()); // 非严格模式下通常不是 10
判断箭头函数的 this,只需看它定义时所在的词法作用域中 this 是什么。如果外层是普通函数,就跟着外层函数的 this 走;如果外层是全局,就遵循全局 this 规则。这一特点与普通函数的动态 this 形成鲜明对比。
四、常见陷阱与调试建议
回调函数是 this 丢失的重灾区。事件监听回调中,浏览器通常把 this 绑定到触发事件的元素;但如果在回调内部再定义普通函数,内层函数的 this 会丢失。解决办法是使用箭头函数、在外部保存 const self = this、或用 bind 显式绑定。
定时器回调同样如此。setTimeout 执行的回调默认是普通函数调用,this 在非严格模式下指向全局对象。使用箭头函数可以沿用外层 this,使用 bind 也能固定上下文。类字段中的箭头函数还可以直接作为事件监听器,既保留 this 指向,又避免在构造函数中手动绑定。
class Button {
constructor() {
this.label = '提交';
document.querySelector('#btn').addEventListener('click', this.handleClick);
}
handleClick = () => {
console.log(this.label);
};
}
实际调试时,可以在函数内部打印 this,观察调用栈。若遇到不确定的 this,先找调用位置,再按优先级套用绑定规则。箭头函数则直接看外层作用域。通过这种方式建立统一的判断流程,比死记硬背各个 API 的行为更可靠。
JavaScript thisthis 指向绑定规则修改时间:2026-08-26 15:54:04