在JavaScript里,this是一个在函数执行阶段才确定的上下文引用,它并不取决于函数写在哪儿,而取决于函数被谁以什么形式调用。很多运行时错误都源于对this绑定时机的误判,比如把方法当回调传出去后内部取不到预期对象。理解this,核心就是看清调用栈顶端的调用关系。

一、默认绑定与严格模式差异
当函数被直接调用,没有任何调用者对象前缀时,就会发生默认绑定。在非严格模式下,浏览器环境里this会指向全局对象window,Node环境指向global。这种绑定最容易被忽略,也是污染全局变量的常见原因。
一旦代码运行在严格模式(即在函数或文件头声明了use strict),独立调用的函数内部this会变成undefined,而不是全局对象。这能帮我们更早暴露错误,避免无意中修改全局状态。下面两段代码对比了两种模式下的表现。
// 非严格模式
function showThis() {
console.log(this); // 浏览器中输出 window
}
showThis();
// 严格模式
'use strict';
function showStrictThis() {
console.log(this); // 输出 undefined
}
showStrictThis();
二、隐式绑定与方法调用
如果函数作为某个对象的属性被调用,即“对象.方法()”形式,this就会隐式绑定到该对象。此时函数内部的this引用的是调用时刻点号左侧的那个容器。这种规则非常直观,但要注意“丢失隐式绑定”的情况。
当我们把对象的方法赋值给一个变量,或者作为回调传给另一个函数,再执行时就已经脱离了原对象,变成默认绑定。下面示例展示了方法被抽取后this指向的变化,以及用闭包保留上下文的一种朴素做法。
const user = {
name: 'Tom',
greet: function() {
console.log('你好,我是' + this.name);
}
};
user.greet(); // 输出:你好,我是Tom
const looseGreet = user.greet;
looseGreet(); // 非严格下 this 为 window,name 为空
// 用闭包固定上下文
const boundGreet = function() {
const self = user;
console.log('你好,我是' + self.name);
};
boundGreet();
三、显式绑定 call、apply 与 bind
JavaScript提供了三个原型方法让我们手动指定this:call和apply立刻执行函数并绑定this,差别只在参数传递方式;bind则返回绑定了this的新函数,不立即执行。它们都属于显式绑定,优先级高于隐式绑定。
在实际开发中,借用数组方法处理类数组对象、React类组件绑定事件处理函数,都会频繁用到它们。下面的代码演示了如何用call让伪数组使用数组的slice,以及用bind提前绑定回调函数中的this。
function sum(a, b) {
console.log(this.prefix + (a + b));
}
const obj = { prefix: '结果:' };
sum.call(obj, 2, 3); // 输出:结果:5
sum.apply(obj, [2, 3]); // 输出:结果:5
const boundSum = sum.bind(obj);
boundSum(4, 5); // 输出:结果:9
四、new绑定与构造函数
当函数通过new关键字调用时,JavaScript会创建一个空对象,将this绑定到该对象,并执行函数体,若未返回其他对象则返回这个this。这就是new绑定,优先级在四种规则中最高。它模拟了传统面向对象语言的实例化过程。
需要注意的是,箭头函数不能作为构造函数使用,因为其没有自己的this,new操作符无法为其绑定实例。下面的例子展示了一个普通构造函数内部this的变化,以及返回自定义对象时会覆盖默认this实例的行为。
function Person(name) {
this.name = name;
}
const p = new Person('Lucy');
console.log(p.name); // 输出 Lucy
function Special() {
this.val = 1;
return { val: 2 }; // 返回对象覆盖this
}
const s = new Special();
console.log(s.val); // 输出 2
五、箭头函数与词法this
箭头函数是ES6引入的特殊函数,它没有自己的this绑定,而是沿用词法作用域中的this,也就是定义时外层函数的this。因此箭头函数内部的this在书写那一刻就定死了,不受调用方式影响,也不会被call改变。
这个特性让箭头函数特别适合用作定时器回调或事件处理器,避免反复bind。但也要小心:如果在顶层定义箭头函数,它的this会直接继承全局或模块作用域。下面展示用箭头函数解决setTimeout中this丢失的经典问题。
const counter = {
count: 0,
start: function() {
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
};
counter.start(); // 每秒输出递增数字
六、优先级与综合判断
遇到复杂调用时,可按优先级顺序判断this:new绑定大于显式绑定,显式绑定大于隐式绑定,隐式绑定大于默认绑定。如果同时出现多种迹象,就以最高优先级为准。箭头函数则直接无视这套规则,看外层。
掌握这些后,建议读代码时先找函数执行的那一行,看它左边有没有点、前面有没有new、有没有被call类方法包过,再决定this是谁。多写几个小例子在控制台打印,比死记理论更有效。
| 调用类型 | this指向 | 示例 |
|---|---|---|
| 默认调用 | 全局对象或undefined | fn() |
| 隐式调用 | 调用者对象 | obj.fn() |
| 显式调用 | 指定对象 | fn.call(obj) |
| new调用 | 新实例 | new fn() |
| 箭头函数 | 外层this | () => {} |
JavaScriptthiscall_stack修改时间:2026-08-09 22:06:42