JavaScript 中 this 的真相:由调用方式决定的动态指针
在 JavaScript 中,this 是一个看似简单却极易被误解的概念。它不像其他语言中的 this 那样固定指向当前对象,而是一个运行时绑定 的动态指针------它的值完全取决于函数如何被调用 ,而非在哪里定义。
本文将从一段典型代码出发,深入剖析 this 的行为逻辑,并梳理其在不同场景下的指向规则,帮助你彻底掌握这一核心机制。
一、一个"反直觉"的现象
考虑如下代码:
ini
var bar = {
myName: "time.geekbang.com",
printName: function() {
console.log(this.myName);
}
};
let myName = '极客邦';
let _printName = bar.printName;
_printName(); // 输出 undefined(非严格模式下)
bar.printName(); // 输出 "time.geekbang.com"
为什么同一个函数,两次调用结果却完全不同?

关键在于:
bar.printName()是作为对象方法调用 ,此时this指向bar;_printName()是作为普通函数调用 ,此时this指向全局对象(浏览器中为window),而window.myName并未定义(注意:let声明的变量不会挂载到window上),所以输出undefined。
这揭示了 this 的本质:它不关心函数在哪定义,只关心函数怎么被调用。
二、this 的四种常见绑定规则
1. 默认绑定(普通函数调用)
scss
function foo() {
console.log(this); // 非严格模式:window;严格模式:undefined
}
foo();
- 在非严格模式下,
this指向全局对象; - 在严格模式(
'use strict')下,this为undefined,避免意外污染全局。
⚠️ 这也是早期 JavaScript 被诟病的设计之一:函数本可独立存在,却强制赋予一个无意义的
this。
2. 隐式绑定(对象方法调用)
javascript
const obj = {
name: '极客时间',
greet() {
console.log(this.name);
}
};
obj.greet(); // "极客时间"
当函数通过 obj.fn() 形式调用时,this 自动绑定到 obj。
⚠️ 丢失绑定:一旦将方法赋值给变量或传入回调,就会退化为默认绑定:
ini
const fn = obj.greet;
fn(); // this 指向 window(或 undefined),输出 undefined
3. 显式绑定(call / apply / bind)
ini
function setName(name) {
this.name = name;
}
const user = {};
setName.call(user, '极客邦');
console.log(user.name); // "极客邦"
通过 call、apply 或 bind,我们可以手动指定 this 的值,实现灵活的上下文控制。
4. 构造函数调用(new 绑定)
ini
function Person(name) {
this.name = name;
}
const p = new Person('极客时间');
console.log(p.name); // "极客时间"
使用 new 调用函数时,JavaScript 引擎会:
- 创建一个新对象;
- 将
this绑定到该对象; - 执行构造函数;
- 返回新对象。
此时 this 指向新创建的实例。
三、特殊场景:事件处理器中的 this
在 DOM 事件处理中,this 指向触发事件的元素:
xml
<a href="#" id="link">点击我</a>
<script>
document.getElementById("link").addEventListener("click", function() {
console.log(this); // <a id="link">...</a>
});
</script>
这是浏览器引擎自动完成的绑定,属于隐式绑定的一种变体。
四、如何安全地访问对象属性?
回到最初的问题:如何确保在方法内部正确访问对象自身的属性?
答案是:始终通过 this 访问,并确保调用方式不会导致 this 丢失。
若需解耦方法引用,可使用以下策略:
- 使用箭头函数(但注意:箭头函数没有自己的
this,会继承外层作用域); - 使用
.bind()提前绑定上下文; - 在类或对象中使用方法引用时,避免直接赋值,改用闭包或代理。
例如:
ini
const safePrint = bar.printName.bind(bar);
safePrint(); // 正确输出 "time.geekbang.com"
结语
this 是 JavaScript 中少有的运行时动态绑定机制,它打破了"词法作用域"的常规思维。理解其绑定规则,不仅能避免常见 bug,还能更自如地操控函数执行上下文。
记住一句话:
this不是你写在哪,而是你叫谁来执行。
掌握这一点,你就真正迈入了 JavaScript 进阶之门。