JavaScript深度继承和超级构造函数

3

我正在尝试弄清JavaScript中的超级(super)是如何工作的。 我有一些想法,但不确定它们的准确性,所以需要帮助。

class A {

}

class B extends A {
    constructor() {
        super();
    }
}

class C extends B {
    constructor() {
        super();
    }
}

new C

在上述代码中,'new'运算符创建一个对象并将其链接到构造函数C的原型,构造函数C的隐藏属性'thisArg'现在指向新创建的对象。
+---------------------+
|   function object   |
+---------------------+
| - code              |
+---------------------+
| - outerScope        |
+---------------------+
| - thisArg           |
+---------------------+
| - [[Prototype]]     |
+---------------------+
| + prototype         |
+---------------------+
| + length            |
+---------------------+
| + name              |
+---------------------+

注意:隐藏属性“thisArg”可以在每个函数调用中通过隐式(使用访问运算符“.”和“[]”)、显式(使用.call、.apply、.bind方法)或“new”运算符进行更改。
词法环境的“this”引用将指向无论函数隐藏属性“thisArg”指向何处。
回到过程中,构造函数C被执行,然后super将新创建的对象传递给B构造函数,构造函数B的隐藏属性"thisArg"现在指向新创建的对象。
构造函数B也是如此,它被执行并且super将新创建的对象传递给A构造函数,其中构造过程开始。
当A完成构造时,它将对象传递给B,然后B将更多的插槽添加到结构体中,并传递给C。
最后,C结束构造并返回构造好的对象。
所以基本上,新创建的对象首先从构造函数冒泡到构造函数,然后向下滴漏?

1
基本上是的。你读过“this”关键字如何工作?中的super部分和规范了吗?值得指出的是,在执行new C时,构造函数内部的thisC的一个实例(永远不是AB),并且只有在链中最后一个super调用之后才能访问this - Sebastian Simon
1
@SebastianSimon - 实际上并不是这样的。只有在调用A时才会创建对象。上面的代码对于使用function的旧方法来说是接近的,但对于使用class的情况则不然。 - T.J. Crowder
1
@SebastianSimon - 对的--但是上面的解释说this在调用C之前被创建,这是不正确的。 - T.J. Crowder
@T.J.Crowder 中的 'this' 在词法环境中只是一个参数。它的参数由函数对象的 'this' 提供,这是一个隐藏属性,你可以称之为 thisArg。 - Slev7n
@Slev7n - 这可能是术语误解或其他问题。函数(包括构造函数)没有“隐藏的'this'属性”。这里是函数对象内部插槽的列表this是绑定在(非箭头)函数环境记录和模块环境记录中的。还有一个叫做thisArgument的不同东西,它是... - T.J. Crowder
显示剩余3条评论
1个回答

6
这不适用于class。构建它的部分与旧的基于function的设置相似,但在这方面,class的工作方式略有不同。
当你执行new C时,直到调用 A 构造函数之前,对象才被创建。以下是发生的情况:
  1. new 使用 C[[Construct]]内部方法,并将new.target设置为C
    1. C 中在super()之前的任何代码都会运行。此时无法访问 this
    2. C 的代码使用仍将new.target设置为CB 的 [[Construct]](通过 super())进行调用。
      1. B 中在super()之前的任何代码仍然可以无法访问this
      2. B 调用 A 的[[Construct]](通过 super()), 仍将new.target设置为C
        1. 由于 A 是基础构造函数, A 的[[Construct]] 创建对象,并根据 new.target(即 C)的prototype属性将其原型设置为该对象。
        2. A 构造函数中的任何代码都会运行,并且可以访问 this
      3. B 中在super()之后的任何代码都会运行(并且可以访问 this)。
    3. C 中在super()之后的任何代码都会运行(并且可以访问 this)。
  2. A 创建的对象是 new 表达式的结果。
(上面为了清晰起见略去了一些细节)
这就是class构造如何确保新对象从基类构造函数(A)到第一个派生构造函数(B)以及最后的new目标(C)的顺序初始化。这样,A最先访问新对象,然后是B,最后是C
更多详细信息请参见上面的规范链接。

1
如果你感兴趣的话,我最近出版的书《JavaScript:新玩具》第四章详细介绍了ES2015-ES2020中的所有内容。我的个人资料中有链接。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接