深入理解 JavaScript 中的 “this”:从自由变量到绑定规则

🧠 深入理解 JavaScript 中的 this:从自由变量到绑定规则

"this 是 JavaScript 最容易被误解的概念之一 ------ 它不是由函数定义决定的,而是由调用方式决定的。"

在日常开发中,我们经常遇到这样的困惑:

  • 为什么同一个函数,有时 this 指向对象,有时却指向 window
  • 为什么在事件回调里 this 是 DOM 元素,而赋值后调用就变成全局对象?
  • 严格模式下 this 为什么会变成 undefined

本文将结合你可能写过的代码,系统梳理 this 的设计逻辑、绑定规则与常见陷阱,并告诉你:如何真正掌控 this


🔍 一、this 不是"自由变量"------它和作用域无关!

很多初学者会混淆 变量查找this 绑定,认为它们是一回事。其实:

特性 自由变量(如 myName this
查找时机 编译阶段(词法作用域) 执行阶段(动态绑定)
查找依据 函数定义位置(Lexical Scope) 函数调用方式
是否受作用域链影响 ✅ 是 ❌ 否

来看一段典型代码:

Js 复制代码
'use strict'; // 严格模式
var myName = '极客邦'; // 挂载到 window

var bar = {
    myName: 'time.geekbang.com',
    printName: function () {
        console.log(myName);       // ✅ 自由变量 → '极客邦'(全局)
        console.log(this.myName);  // ❓ this 取决于怎么调用!
    }
};

function foo() {
    let myName = '极客时间';
    return bar.printName;
}

var _printName = foo();
_printName();      // this → undefined(严格模式)
bar.printName();   // this → bar
  • myName 是自由变量,按词法作用域 查找 → 总是取全局的 '极客邦'
  • this.myName 的值完全取决于函数如何被调用

💡 关键结论:this 和作用域链毫无关系!它是运行时的"调用上下文"决定的。


🎯 二、this 的五种绑定规则(优先级从高到低)

1️⃣ 显式绑定(Explicit Binding)

使用 call / apply / bind 强制指定 this

Js 复制代码
function foo() {
    this.myName = '极客时间';
}
let bar = { name: '极客邦' };
foo.call(bar); // this → bar
console.log(bar); // { name: '极客邦', myName: '极客时间' }

最高优先级,直接覆盖其他规则。


2️⃣ 隐式绑定(Implicit Binding)

作为对象的方法 调用 → this 指向该对象

Js 复制代码
var myObj = {
    name: '极客时间',
    showThis: function() {
        console.log(this); // → myObj
    }
};
myObj.showThis(); // 隐式绑定

⚠️ 陷阱:隐式丢失(Implicit Loss)

Js 复制代码
var foo = myObj.showThis; // 函数引用被赋值
foo(); // 普通函数调用 → this = window(非严格)或 undefined(严格)

这就是为什么 setTimeout(myObj.method, 1000) 会丢失 this


3️⃣ 构造函数绑定(new Binding)

使用 new 调用函数 → this 指向新创建的实例

Js 复制代码
function CreateObj() {
    this.name = '极客时间';
}
var obj = new CreateObj(); // this → obj

内部机制相当于:

Js 复制代码
var temObj = {};
temObj.__proto__ = CreateObj.prototype;
CreateObj.call(temObj);
return temObj;

4️⃣ 普通函数调用(Default Binding)

既不是方法,也没用 newcall → 默认绑定

  • 非严格模式this → window(浏览器)或 global(Node)
  • 严格模式this → undefined
Js 复制代码
function foo() {
    console.log(this); // 非严格 → window;严格 → undefined
}
foo();

🚫 这是 JS 的一个"历史包袱":作者 Brendan Eich 当年为了快速实现,让普通函数的 this 默认指向全局对象,导致大量意外污染。


5️⃣ 箭头函数(Arrow Function)

没有自己的 this !继承外层作用域的 this

Js 复制代码
class Button {
    constructor() {
        this.text = '点击';
        document.getElementById('btn').addEventListener('click', () => {
            console.log(this.text); // ✅ 正确指向 Button 实例
        });
    }
}

✅ 箭头函数是解决"回调中 this 丢失"的利器,但不能用于需要动态 this 的场景(如构造函数、对象方法)。


🌐 三、特殊场景:DOM 事件中的 this

Html 复制代码
<a href="#" id="link">点击我</a>
<script>
document.getElementById('link').addEventListener('click', function() {
    console.log(this); // → <a id="link"> 元素
});
</script>

这是 addEventListener 的规范行为

回调函数中的 this 自动绑定为注册事件的 DOM 元素

但注意:

  • 如果用箭头函数 → this 不再是元素!
  • 如果把函数赋值给变量再调用 → 隐式丢失!

⚠️ 四、为什么 this 的设计被认为是"不好"的?

  1. 违反直觉 :函数定义时看不出 this 指向谁。
  2. 容易出错:隐式丢失、全局污染频发。
  3. 依赖调用方式 :同一函数,不同调用,this 不同。

正因如此,ES6 引入了 class 和箭头函数,弱化对 this 的依赖


✅ 五、最佳实践建议

场景 推荐做法
对象方法 用普通函数,避免赋值导致隐式丢失
回调函数(如事件、定时器) 用箭头函数,或 .bind(this)
构造函数 class 替代传统函数
需要强制指定上下文 call / apply / bind
避免全局污染 使用严格模式 + let/const

🔚 结语

this 并不神秘,它只是 JavaScript 动态绑定机制的一部分。理解它的核心在于:

"谁调用了这个函数,this 就是谁。"

掌握五种绑定规则,避开隐式丢失陷阱,你就能在任何场景下准确预测 this 的指向。

最后记住:现代 JavaScript 已经提供了更安全的替代方案(如 class、箭头函数),不必死磕 this ------ 但你必须懂它。


📚 延伸阅读

  • 《你不知道的JavaScript(上卷)》------ "this & 对象原型"章节
  • MDN: this
  • ECMAScript 规范:Function Calls

欢迎在评论区分享你踩过的 this 坑! 👇

如果觉得有帮助,别忘了点赞 + 关注~ ❤️


标签:#JavaScript #this #前端 #作用域 #掘金

相关推荐
北极象1 小时前
Electron + Playwright 一文多发应用架构设计
前端·javascript·electron
咖猫1 小时前
guacamole-web 1.5.5 index.html
前端·javascript·html
getapi1 小时前
Express 是一个基于 Node.js 的轻量级、灵活的 Web 应用框架,广泛用于构建后端服务和 API
前端·node.js·express
渣波1 小时前
🧳 我的 React Trip 之旅(5):我的 AI 聊天机器人,今天又把用户气笑了
前端·javascript
boombb1 小时前
数据驱动与CSS预定义样式:实现灵活多变的Banner布局
前端
JIngJaneIL1 小时前
基于Java失物招领系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·vue
鼎道开发者联盟1 小时前
当界面会思考:AIGUI八要素驱动DingOS实现“感知-生成-进化“闭环
前端·人工智能·ai·gui
豐儀麟阁贵1 小时前
9.3获取字符串信息
java·开发语言·前端·算法
苦夏木禾1 小时前
使用css制作一个环形进度展示圈
前端·css