JavaScript的秘密生活:身份

发布: (2026年1月13日 GMT+8 15:02)
5 min read
原文: Dev.to

Source: Dev.to

“点左侧”规则

Timothy 懒散地坐到主工作台的椅子上,把笔掉在一段代码上。他看起来很疲惫。

“我已经不再知道自己是谁了,Margaret,”他嘀咕道。

Margaret 停下手中的整理,走了过来。

“那是个深刻的哲学问题,Timothy。”
“这不是哲学,是这个函数,”他敲了敲纸张说,“我在 user 对象里写了一个 printName 函数。运行时它会打印 ‘Timothy’。但当我把完全相同的函数传给一个助手时,它忘记了自己是谁,打印出 undefined。它正经历身份危机。”

Margaret 把一块滚动黑板拉到桌子旁,拿起一支粉笔。

“函数并没有危机。你只是错误地认为 this 属于函数本身。事实并非如此。”

她在黑板上画了一个大函数。

“在 JavaScript 中,this 不是一个固定的标签。它是一个问题。当代码运行时,函数会环顾四周并自问:‘谁调用了我?’”

示例:方法调用

const user = {
    name: "Timothy",
    speak: function() {
        console.log("My name is " + this.name);
    }
};

user.speak(); 
//  ^ 看向左侧
// 对象 'user' 正在调用该函数。
// 因此:'this' 是 'user'。

“看最后一行,”Margaret 指着点说,“规则很简单:看点左侧。”
user 这个词就在那儿,”Timothy 回答。
“没错。因为你是通过 user 调用它的,函数就会用‘谁调用了我?’这个问题的答案‘user’来回答。”

示例:丢失的上下文

Timothy 把他的 bug 写在黑板上。

const myFunction = user.speak;

myFunction(); 
//  ^ 看向左侧
// 没有点。没有对象。
// 输出: "My name is undefined"

“我没有改动函数内部的代码!”Timothy 辩解道。“是同一个函数!”
“函数内部的代码确实没有变,”Margaret 同意,“但调用位置变了。”
“看看 myFunction() 左侧,有点吗?有对象吗?”
“没有。只有函数名。”
“正是如此。当没有点时,函数没有所有者。在我们始终使用的严格模式下,this 会变成 undefined。”
“那以前呢?”
“会默认指向全局的 window 对象——这是一场灾难的前奏。”

强制上下文

使用 .call() 的一次性调用

const stranger = { name: "Margaret" };

// 我们强制让 `speak` 此时使用 `stranger` 作为 `this`
user.speak.call(stranger);
// 输出: "My name is Margaret"

“使用 .call(),你是在告诉函数:‘我不在乎你现在在哪里。对于这一次特定的执行,你的身份就是这个对象。’”

使用 .bind() 的永久绑定

// 我们创建一个新函数,永久锁定到 `user`
const boundFunction = user.speak.bind(user);

boundFunction();
// 输出: "My name is Timothy"(永远如此)

.bind() 并不会立即执行函数。它返回一个新的函数副本,永远记住它的所有者。无论以后怎么调用,this 始终是 user。”

总结

  • 有点吗?this 是左侧的对象。
  • 没有点?this 在严格模式下是 undefined,在非严格模式下是全局对象。
  • 使用了 .call().bind()this 是你显式设置的对象。

“我以为这跟函数所在的位置有关,”Timothy 承认道。
“那是常见的误解,”Margaret 边把手上的粉笔抖落边说,“在 JavaScript 中,身份并不是关于你是谁,而是关于在你说话的那一刻,谁在握着你。”

Back to Blog

相关文章

阅读更多 »