Internals:参考类型深入的语言功能
本节涵盖一个高级主题,以更好地理解某些极端情况。
如果您想更快地进行,可以跳过或推迟。
一个复杂的方法调用可能会丢失这个,例如:
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // John (the simple call works)
// now let's call user.hi or user.bye depending on the name
(user.name == "John" ? user.hi : user.bye)(); // Error!
在最后一行有一个条件运算符,可以选择 user.hi 或 user.bye。在这种情况下,结果是 user.hi。
然后用括号()立即调用该方法。但它不能正常工作!
如您所见,调用导致错误,因为调用内部“this”的值未定义。
这可行(对象点法):
user.hi();
这不(评估方法):
(user.name == "John" ? user.hi : user.bye)(); // Error!
为什么?如果我们想了解它发生的原因,让我们深入了解 obj.method() 调用的工作原理。
仔细观察,我们可能会注意到 obj.method() 语句中有两个操作:
First, the dot '.' retrieves the property obj.method.
Then parentheses () execute it.
那么,关于这方面的信息是如何从第一部分传递到第二部分的呢?
如果我们将这些操作放在不同的行上,那么这肯定会丢失:
let user = {
name: "John",
hi() { alert(this.name); }
}
// split getting and calling the method in two lines
let hi = user.hi;
hi(); // Error, because this is undefined
这里hi = user.hi将函数放入变量中,然后在最后一行是完全独立的,所以没有这个。
为了使user.hi() 调用有效,JavaScript 使用了一个技巧——点 '.'返回的不是函数,而是特殊引用类型的值。
引用类型是“规范类型”。我们不能显式使用它,但它是由语言内部使用的。
Reference Type的值为三值组合(base、name、strict),其中:
base 是对象。
name 是属性名称。
如果 use strict 生效,则 strict 为真。
属性访问user.hi的结果不是一个函数,而是一个Reference Type的值。对于严格模式下的 user.hi,它是:
// Reference Type value
(user, "hi", true)
在引用类型上调用括号 () 时,它们会收到有关对象及其方法的完整信息,并且可以设置正确的 this(=user 在这种情况下)。
引用类型是一种特殊的“中介”内部类型,目的是从 dot 传递信息。调用括号 ()。
像赋值hi = user.hi 这样的任何其他操作都会将引用类型作为一个整体丢弃,取user.hi(一个函数)的值并将其传递。所以任何进一步的操作都会“失去”这个。
因此,只有在使用点 obj.method() 或方括号 obj'method' 语法直接调用函数时, this 的值才会以正确的方式传递(它们在此处执行相同的操作)。