从规范的角度看 this 丢失 —— 详解 Reference Type

简介: 从规范的角度看 this 丢失 —— 详解 Reference Type

640.png


Reference Type


深入的语言特性


本文所讲的是一个高阶主题,能帮你更好地理解一些边缘情况。

这仅是锦上添花。许多经验丰富的的开发者不甚了了也过得不错。如果你想了解代码运行的本质,那就继续读下去吧。

一个动态执行的方法调用可能会丢失 this


例如:


let user = {
  name: "John",
  hi() { alert(this.name); },
  bye() { alert("Bye"); }
};
user.hi(); // 正常运行
// 现在让我们基于 name 来选择调用 user.hi 或 user.bye
(user.name == "John" ? user.hi : user.bye)(); // Error!


在最后一行有个在 user.hiuser.bye 中做选择的条件(三元)运算符。当前情形下的结果是 user.hi


接着该方法被通过 () 立刻调用。但是并不能正常工作!


如你所见,此处调用导致了一个错误,因为在该调用中 "this" 的值变成了 undefined


这样是能工作的(对象.方法):


user.hi();


这就无法工作了(被评估的方法):


(user.name == "John" ? user.hi : user.bye)(); // Error!


为什么呢?欲知缘何,且让我们深入 obj.method() 调用运行的本质。


Reference type 解读


仔细看的话,我们可能注意到 obj.method() 语句中的两个操作:


  1. 首先,点 '.' 取了属性 obj.method 的值。
  2. 接着 () 执行了它。


那么,this 的信息是怎么从第一部分传递到第二部分的呢?

如果我们将这些操作放在不同的行,this 必定是会丢失的:


let user = {
  name: "John",
  hi() { alert(this.name); }
}
// 把获取方法和调用方法拆成两行
let hi = user.hi;
hi(); // 报错了,因为 this 的值是 undefined


这里 hi = user.hi 把函数赋值给了一个变量,接下来在最后一行它是完全独立的,所以这里没有 this


为确保 user.hi() 调用正常运行,JavaScript 玩了个小把戏 —— 点 '.' 返回的不是一个函数,而是一个特殊的 Reference Type[1] 的值。


Reference Type 是 ECMA 中的一个“规范类型”。我们不能直接使用它,但它被用在 JavaScript 语言内部。


Reference Type 的值是一个三个值的组合 (base, name, strict),其中:


  • base 是对象。
  • name 是属性名。
  • strictuse strict 模式下为 true。


对属性 user.hi 访问的结果不是一个函数,而是一个 Reference Type 的值。对于 user.hi,在严格模式下是:


// Reference Type 的值
(user, "hi", true)


() 被在 Reference Type 上调用时,它们会接收到关于对象和对象的方法的完整信息,然后可以设置正确的 this(在此处 =user)。


Reference Type 是一个特殊的“中间人”内部类型,目的是从 . 传递信息给 () 调用。


任何例如赋值 hi = user.hi 等其他的操作,都会将 Reference Type 作为一个整体丢弃掉,而会取 user.hi(一个函数)的值并继续传递。所以任何后续操作都“丢失”了 this


因此,this 的值仅在函数直接被通过点符号 obj.method() 或方括号 obj['method']( "'method'") 语法(此处它们作用相同)调用时才被正确传递。在本教程的后续章节,我们会学习多种解决这个问题的方式,例如 func.bind()[2]


总结


Reference Type 是语言内部的一个类型。


读取一个属性,例如在 obj.method() 中,. 返回的准确来说不是属性的值,而是一个特殊的 "Reference Type" 值,其中储存着属性的值和它的来源对象。


这是为了随后的方法调用 () 获取来源对象,然后将 this 设为它。


对于所有其它操作,Reference Type 会自动变成属性的值(在我们这个情况下是一个函数)。


这整个机理对我们是不可见的。它仅在一些微妙的情形下才重要,例如一个方法是通过表达式从对象动态获取的。

目录
相关文章
|
6月前
|
C#
c#之Attribute特性的原理
c#之Attribute特性的原理
26 0
|
6月前
Attribute特性的原理
Attribute特性的原理
28 0
|
JSON Java 数据库
代码重构实战-将值对象改为引用对象(Change Value to Reference)
一个数据结构中可能包含多个记录,而这些记录都关联到同一个逻辑数据结构。例如,我可能会读取一系列订单数据,其中有多条订单属于同一个顾客。遇到这样的共享关系,既能将顾客信息作为值对象看待,也能将其视为引用对象
82 0
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(2)
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(2)
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(2)
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(1)
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(1)
SAP WM 使用Storage Location Reference实现IM层面的存储地点和WM层面的存储类型之间的软关联(1)
重构——29以数据类取代记录(Replace Record with Data Class)
以数据类取代记录(Replace Record with Data Class):你需要面对传统编程环境中的记录结构;为该记录创建一个“哑”数据对象
1524 0
|
Java 编译器 程序员
谈谈C++新标准带来的属性(Attribute)
从C++11开始,标准引入了一个新概念“属性(attribute)”,本文将简单介绍一下目前在C++标准中已经添加的各个属性以及常用属性的具体应用。
谈谈C++新标准带来的属性(Attribute)
|
程序员
Attribute(特性),怎么用才更好? —— 字段编号被误解了
  上一篇里(Attribute(特性),怎么用才更好? ),有人说,“坚决杜绝magic number ”,这个magic number指的就是字段编号吧,其实您误解了。   一提到字段编号,可能有些人的第一反应就是这样的用法:     Person1.2000020,或者Person1[2000020],或者ds[2000020]。
858 0