this是JavaScript中最容易让人困惑的概念之一,同样是调用一个函数,this可能指向全局对象、某个对象、甚至undefined。很多初学者会凭直觉认为this指向函数本身,或者指向函数定义时所在的作用域,这两种理解其实都是错误的。本文从函数调用的底层机制出发,把this的绑定规则彻底讲清楚。
this并不是在函数定义的时候就确定下来的,而是在函数被调用的时候才绑定。也就是说,同一个函数在不同位置调用,this的值可以完全不同。理解了这一点,就掌握了分析this的第一把钥匙:看函数是如何被调用的,而不是看它是在哪里声明的。

四条绑定规则:判断this的核心依据
JavaScript规范中规定了四条this绑定规则,按照优先级从低到高分别是默认绑定、隐式绑定、显式绑定和new绑定。判断一段代码中this的指向,本质上就是判断这次函数调用命中了哪条规则。
默认绑定:独立函数调用
当函数被独立调用,也就是不带任何修饰符直接调用时,走的是默认绑定。在非严格模式下,this指向全局对象,浏览器环境中是window,Node环境中是global。而在严格模式下,this是undefined,访问this上的属性会直接抛出错误。
function foo() {
console.log(this);
}
foo(); // 非严格模式下输出 window,严格模式下输出 undefined
function bar() {
'use strict';
console.log(this);
}
bar(); // undefined需要注意的是,即使函数定义在某个对象内部,只要调用时是独立调用的形式,依然是默认绑定。比如把对象的方法赋值给一个变量再调用,this就会丢失原来的绑定对象,退回默认绑定。这是开发中非常常见的坑。
隐式绑定:通过对象调用
如果函数调用时带有上下文对象,比如obj.fn()这种形式,就走隐式绑定,this指向调用时的那个对象。这里的关键是看调用位置,也就是点号前面是谁,this就指向谁。
const obj = {
name: '前端学习者',
sayName() {
console.log(this.name);
}
};
obj.sayName(); // 前端学习者,this指向obj
const fn = obj.sayName;
fn(); // undefined,this退回默认绑定指向window隐式绑定还有一个经典的丢失问题:隐式绑定的对象只有一层有效。对于obj1.obj2.fn()这种链式调用,this指向的是最后一层的obj2,而不是obj1。另外回调函数传参时也常常发生绑定丢失,比如把对象方法作为setTimeout的回调传入,执行时this就不再是原来的对象了。
显式绑定:call、apply和bind
当隐式绑定不够用,或者需要强制指定this的时候,可以使用call、apply和bind这三个方法进行显式绑定。call和apply的功能几乎一样,都是立即调用函数并指定this,区别只在于传参方式:call接受参数列表,apply接受一个参数数组。bind则不同,它不会立即调用函数,而是返回一个永久绑定了this的新函数,之后无论怎么调用这个新函数,this都不会再改变。
function introduce(city, hobby) {
console.log(`我叫${this.name},住在${city},爱好是${hobby}`);
}
const person = { name: '小明' };
introduce.call(person, '北京', '编程');
introduce.apply(person, ['上海', '读书']);
const bound = introduce.bind(person, '广州');
bound('跑步'); // 我叫小明,住在广州,爱好是跑步显式绑定还能进一步派生出硬绑定。当bind之后的新函数再用new调用时,new的优先级更高,this会指向新创建的对象,bind指定的this会被忽略。利用bind可以解决前面提到的回调丢失绑定的问题,比如setTimeout(obj.sayName.bind(obj), 100),回调执行时this依然是obj。
new绑定:构造函数调用
使用new关键字调用函数时,会发生四步操作:创建一个全新的对象,将这个对象的内部原型指向函数的prototype,把this绑定到这个新对象上并执行函数体,如果函数没有显式返回对象就返回这个新对象。所以在构造函数内部,this指向的就是即将返回的新实例。
function User(name) {
// new调用时,this指向新创建的对象
this.name = name;
console.log(this);
}
const u = new User('张三'); // this指向User的实例
console.log(u.name); // 张三绑定规则的优先级与判断流程
四条规则同时存在时如何取舍?实际开发中不需要四条都考虑,因为它们的适用条件互斥程度比较高,真正需要比较的是显式绑定和new绑定。结论是:new绑定优先于显式绑定,显式绑定优先于隐式绑定,隐式绑定优先于默认绑定。可以用bind的例子验证:bind返回的函数再用new调用,this指向新对象而不是bind的参数,说明new绑定优先级更高。
据此可以整理出一套判断流程:先看函数是不是箭头函数,如果是,this由外层作用域决定,与调用方式无关;如果不是,再看是不是用new调用的,是则this指向新创建的对象;接着看是否通过call、apply或bind指定了this,是则指向指定的对象;再看调用位置是否有上下文对象,有则指向该对象;最后兜底走默认绑定,非严格模式指向全局对象,严格模式为undefined。
这套流程可以解决绝大多数场景。面试中常见的变形题,比如对象方法赋值后调用、链式调用、定时器回调、事件处理函数等,都可以逐层套用这个流程分析,不需要死记硬背结论。
箭头函数与this指向的特殊之处
箭头函数是ES6引入的语法,它彻底不适用上述四条规则。箭头函数没有自己的this,它的this继承自定义时所在外层作用域的this,而且在定义时就确定了,之后任何方式都无法改变,对箭头函数使用call、apply、bind传入的this会被直接忽略。
const obj = {
name: 'obj',
sayName: () => {
console.log(this.name);
},
sayNameDelay() {
setTimeout(() => {
console.log(this.name);
}, 100);
}
};
obj.sayName(); // 输出空,箭头函数的this指向全局,name不存在
obj.sayNameDelay(); // 100毫秒后输出 obj,继承了sayNameDelay的this上面的例子很好地展示了箭头函数的两面性。直接把箭头函数作为对象方法使用是错误的做法,因为对象字面量本身不构成作用域,箭头函数的this会继承到全局,得不到预期的obj。但在setTimeout回调这类场景中,箭头函数反而非常好用,它继承了外层方法的this,避免了传统function回调绑定丢失的问题。
常见坑点与实践建议
开发中最常踩的坑是this绑定丢失。典型场景有三类:把对象方法赋值给变量后调用、把方法作为回调函数传递、以及在嵌套函数中内层function的this意外指向全局。前两类可以用bind或箭头函数解决,第三类早年流行用const that = this的方式保存引用,现在直接在内层使用箭头函数即可。
还有一个容易被忽视的点是,DOM事件处理函数中的this指向绑定事件的那个DOM元素,而不是定义函数的对象。如果需要访问组件或对象实例,通常要在外层先绑定好this,或者直接改用箭头函数定义事件回调。类组件React时代大量使用constructor中bind方法,解决的正是这个问题。
实践中给出几条建议:类的方法如果需要作为回传递出去,优先用箭头函数属性或bind提前绑定;不要对箭头函数使用call、apply、bind去改this,那是无效操作;分析this时始终回到调用位置,看函数是怎么被调用的,而不是它在哪定义的。把四条规则的优先级和箭头函数的特殊性装进脑子里,this就不再是玄学问题。
this指向JavaScript绑定规则箭头函数修改时间:2026-09-06 11:25:23