网站首页 > 技术文章 正文
您说得对,这个概念确实是 JavaScript 中最让人头疼、也最容易出错的地方之一。技术文档解释得没错,但太抽象了。
我们换个极其通俗的方式来理解,彻底忘掉“动态作用域”这种词。
### 核心比喻:`this` 就是函数里的代词 “我”
想象一下,`this` 就是中文里的 “我”。
**“我是谁?”** 这个问题的答案,完全取决于**“谁在说话”**。
* 如果 **马云** 说:“我是个老师”,那 “我” 指的就是 **马云**。
* 如果 **我** 说:“我是个程序员”,那 “我” 指的就是 **我**。
在 JavaScript 中,**想知道 `this` 是谁,就看是谁在“调用”这个函数**。函数本身只是一个稿子,谁来念这个稿子,稿子里的“我”(`this`)就是谁。
下面我们用这个“谁在说话”的比喻,来重新理解您提到的所有规则。
---
### 1. 隐式绑定:谁是主语,`this` 就是谁
这是最常见的情况。当函数作为一个对象的方法被调用时,`this` 就是那个对象。
**比喻:** “马云.说()” -> 这句话的主语是“马云”,所以里面的“我”就是马云。
**详细举例:**
```javascript
const person = {
name: "小明",
introduce: function() {
// 这是一段讲稿:“大家好,我是 [this.name]”
console.log("大家好,我是" + this.name);
}
};
const company = {
name: "阿里巴巴",
introduce: person.introduce // 公司也拿到了同一份讲稿
};
// 情况一:小明来念稿子
person.introduce(); // 调用者是 person,所以 this 就是 person
// 输出: "大家好,我是小明"
// 情况二:公司来念稿子
company.introduce(); // 调用者是 company,所以 this 就是 company
// 输出: "大家好,我是阿里巴巴"
```
**结论:** 谁调用,`this` 就是谁。`person.introduce()` 点前面的 `person` 就是调用者。
### 2. 默认绑定:没人当主语,`this` 就很尴尬
当一个函数被独立调用,前面没有“主语”时,`this` 就会指向全局对象(在浏览器里是 `window`)。
**比喻:** 你在马路上捡到一张纸条,上面写着:“我是个帅哥”。这张纸条没有署名,也不是某个人递给你的。那这个“我”是谁?没人知道,或者说,它属于这个“世界”(全局环境)。
**详细举例(接上例):**
```javascript
// 你把小明的“介绍能力”(函数)单独拿了出来
const standaloneIntroduce = person.introduce;
// 现在你直接运行这个能力,前面没有任何主语
standaloneIntroduce(); // =>
window.standaloneIntroduce() (非严格模式下)
// 输出: "大家好,我是" (因为 window 对象上没有 name 属性)
```
这就是您例子中 `extractedMethod()` 出现问题的原因。`extractedMethod` 只是一个函数引用,一个“讲稿”,当你直接 `extractedMethod()` 调用时,没有主语,`this` 就跑到了全局。
### 3. 显式绑定:强行指定谁来说话 (`call`, `apply`, `bind`)
有时候我们不希望 `this` 乱跑,想明确指定“稿子里的‘我’到底是谁”。
**比喻:** 你是导演,你拿着剧本(函数),指着一个演员(对象)说:“你!来念这段台词!”
* `.call()` / `.apply()`: **“你,现在就念!”** (立即执行)
* `.bind()`: **“这剧本以后就归你了,你拿着,等我喊你念的时候再念。”** (创建一个绑定好的新函数)
**详细举例(接上例):**
```javascript
const student = { name: "小红" };
// 使用 .call() 强行让小红来念稿子
standaloneIntroduce.call(student);
// 输出: "大家好,我是小红"
// 使用 .bind() 创建一个永远属于小红的专属介绍函数
const introduceForStudent = standaloneIntroduce.bind(student);
// 现在无论何时何地调用这个新函数,里面的“我”永远是小红
introduceForStudent();
// 输出: "大家好,我是小红"
```
### 4. `new` 绑定:创造一个全新的“人”来说话
当使用 `new` 关键字时,它会创建一个全新的空对象,并让函数里的 `this` 指向这个新对象。
**比喻:** 你有一份“新生儿登记表”(构造函数)。当你用 `new` 盖章时,就等于:
1. 创造一个全新的婴儿(新对象)。
2. 把登记表里的“我”(`this`)都指向这个婴儿。
3. 按照登记表上的指示(函数代码)给婴儿设置姓名、身高、体重等。
4. 最后把这个登记好的婴儿交给你。
**详细举例:**
```javascript
function Person(name) {
// 这是一份登记表
// 1. 一个新对象被默默创建了...
// 2. this 指向了这个新对象
this.name = name; // 给这个新对象设置 name 属性
console.log("一个叫 " + this.name + " 的人诞生了。");
// 3. 这个新对象被默默返回了...
}
const xiaohua = new Person("小华"); // new 创造了小华,并让 Person 里的 this 指向小华
// 输出: "一个叫 小华 的人诞生了。"
console.log(xiaohua.name); // "小华"
```
### 特例:箭头函数 => 没有自己的“我”,只会“复读”老板的“我”
箭头函数是ES6的重大革新,它彻底改变了 `this` 的规则。
**核心:箭头函数没有自己的 `this`。它里面的 `this` 是在它被定义时,它外层环境的 `this` 是谁,它就继承谁,并且永远不变。**
**比喻:** 箭头函数就像一个忠诚的“秘书”。秘书自己没有身份,当别人问“你是谁”时,他永远回答:“我是老板的秘书”。他的身份 (`this`) 永远绑定在他老板身上。
**经典详细举例:**
```javascript
const manager = {
name: "王经理",
team: ["小明", "小红"],
// 使用传统函数
assignTask_Wrong: function() {
console.log(this.name); // 这里的 this 是 manager,没问题
setTimeout(function() {
// 问题来了!setTimeout 里的函数是独立调用的(默认绑定)
// 这里的 this 指向了 window,就像那张被捡到的纸条
console.log(`任务分配给:${this.name} 的团队`); // this.name 是 undefined
}, 1000);
},
// 使用箭头函数
assignTask_Correct: function() {
console.log(this.name); // 这里的 this 是 manager
setTimeout(() => {
// 箭头函数是秘书!它在定义时,外面的 this 是 manager
// 所以它就永久继承了 manager 作为自己的 this
console.log(`任务分配给:${this.name} 的团队`); // this.name 是 "王经理"
}, 1000);
}
};
manager.assignTask_Wrong(); // 1秒后输出: "任务分配给: 的团队"
manager.assignTask_Correct(); // 1秒后输出: "任务分配给:王经理 的团队"
```
### 总结:判断 `this` 是谁的优先级
1. **`new` 调用?** `this` 是新创建的对象。
2. **`call`, `apply`, `bind` 调用?** `this` 是被指定的那个对象。
3. **`对象.函数()` 调用?** `this` 就是那个对象。
4. **直接调用?** `this` 是全局对象 (`window`) 或 `undefined` (严格模式)。
5. **是箭头函数?** 忘掉上面所有规则,`this` 等于它外层作用域的 `this`。
希望这个“谁在说话”的比喻能让您彻底理解 `this` 的行为!
猜你喜欢
- 2025-06-24 登录人人都是产品经理即可获得以下权益
- 2025-06-24 这7道问题可以检验你掌握Javascript的真正实力
- 2025-06-24 Python | Django 模板 template(django templates)
- 2025-06-24 Axure高保真教程:通过文本框维护下拉列表选项
- 2024-08-01 javascript 中 null 和 undefined 的区别是什么?
- 2024-08-01 js基础面试题21-30道(50道js面试题)
- 2024-08-01 第31节 类型和对象-Javascript-零点程序员-王唯
- 2024-08-01 js对象关联(js对象类)
- 2024-08-01 一文搞懂JavaScript面向对象(js面向对象的三大特性)
- 2024-08-01 javascript对象#前端(javascript 对象)
- 1506℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 482℃MySQL service启动脚本浅析(r12笔记第59天)
- 460℃启用MySQL查询缓存(mysql8.0查询缓存)
- 455℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 441℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 436℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 418℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 416℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)