JavaScript 中 `this` 的真相:由调用方式决定的动态指针

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')下,thisundefined,避免意外污染全局。

⚠️ 这也是早期 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); // "极客邦"

通过 callapplybind,我们可以手动指定 this 的值,实现灵活的上下文控制。


4. 构造函数调用(new 绑定)

ini 复制代码
function Person(name) {
  this.name = name;
}
const p = new Person('极客时间');
console.log(p.name); // "极客时间"

使用 new 调用函数时,JavaScript 引擎会:

  1. 创建一个新对象;
  2. this 绑定到该对象;
  3. 执行构造函数;
  4. 返回新对象。

此时 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 进阶之门。

相关推荐
0_Error_3 小时前
猫国建设者 修改资源 自动采集
javascript
贩卖黄昏的熊3 小时前
NestJS简明教程——异常处理和日志
javascript·node.js·nest.js
海带紫菜菠萝汤7 小时前
FFmpeg.wasm 实践:在浏览器中运行 FFmpeg 的能力边界与性能瓶颈
前端·javascript·ffmpeg·音视频·wasm
名字还没想好☜7 小时前
React 受控输入框光标跳到末尾:格式化输入时的 selection 丢失 bug 与修复
前端·javascript·react.js·bug·react·next.js
upgrador7 小时前
桌面应用开发:Electron 与 NSIS 的关系、打包流程及 Windows 本地构建实战
javascript·windows·electron
我要两颗404西柚7 小时前
Stage three:VUE工程化与实战工具
前端·javascript·vue.js
写不来代码的草莓熊8 小时前
Cesium 地图交互绘制圆形、多边形
前端·javascript·地图
JackSparrow4149 小时前
前端安全之JS混淆+请求加密+请求签名以提升爬虫难度
前端·javascript·后端·爬虫·python·安全
kyriewen1 天前
我用AI写了半年代码——回头看,这5个能力正在退化
前端·javascript·ai编程
大龄秃头程序员1 天前
iOS 客户端视角扫盲:WKWebView 里 window、messageHandlers 与 Native 回调到底怎么工作?
javascript