我有以下代码:
class Polygon {
constructor() {
this.name = "Polygon";
}
}
class Rectangle {
constructor() {
this.name = "Rectangle";
}
}
class Square extends Polygon {
constructor() {
super();
}
}
Object.setPrototypeOf(Square, Rectangle);
const instance = new Square();
console.log(instance.name); // Rectangle
我的理解是:
Instance__proto__
:指向Square.prototype来继承实例方法。Subclass.__proto__
:指向Rectangle来继承静态方法和属性。Square.prototype.__proto__
:指向Polygon.prototype,用于继承父类的实例方法。
我的问题:
在上面的代码中,使用 之后Object.setPrototypeOf(Square, Rectangle)
,Square._proto_
现在所有静态属性都指向 Rectangle 而不是 Polygon。而对于继承所有其他方法,则Square.prototype._proto_
指向Polygon.prototype
。因此,我希望super()
Square 调用 Polygon 的构造函数,因为 Square 扩展了 Polygon。但是,super() 似乎调用的是 Rectangle 的构造函数。我现在很困惑,Object.setPrototypeOf(Square, Rectangle)
运行时会发生什么变化,似乎我的理解存在差距。
缺少的是价值
super
来自何处。基类(即不扩展其他类)的原型是
Function.prototype
。例如一个类扩展另一个类会导致两件事发生:
扩展类对象的原型是在被扩展的类上而不是在 上
Function.prototype
。例如扩展类的
prototype
属性以被扩展类的属性为原型prototype
。例如请注意,
constructor
从扩展类继承的值prototype
不会被修改,并且仍然设置为扩展类对象。IE这会产生影响:无法根据
constructor
扩展类的实例继承的属性来确定扩展对象的超类。super
以定位超类对象的构造方法。此代码显示了出于实验目的更改类对象原型的效果,以查看会发生什么。这不是现实世界编程中要做的事情。
更新:作为继承的 getter 来返回 null 或另一个已原型化的对象在ECMAScript
__proto__
中被标记为“NORMATIVE OPTIONAL, LEGACY” ,因此被视为已弃用:使用已从测试代码中删除。__proto__
在上述所有情况中,
Polygon
Rectangle
测试代码中的类声明都声明了一个名为的静态属性ClassName
和类方法inheritsFrom
ClassName
属性值是从当时进行原型评估的Square
对象继承的。Square
Square.ClassName
name
是一个由名为 的构造函数设置的实例属性super()
。constructor.name
由类实例从其继承链中的第一个对象继承。Square
实例的继承链从 开始,Square.prototype
然后返回到Polygon.prototype
,其中任何一个都不会被测试代码修改。inheritsFrom
是在类声明中定义的类方法Polygon
,并Rectangle
在测试代码中我将省略有关类、原型和其他内容的大部分细节 - 您可以在 MDN 的对象原型和类页面上阅读有关它们的内容。
但是:通常,在 JS 中,
class
不会像在其他语言中那样预先备份。JS 允许在运行时更改类的定义,因此,如果您愿意,对 的调用可以在整个脚本执行过程中指向不同的构造函数。在这方面,它更像是 Common Lisp 中的类,而不像 Java 中的类。super()
当你写作时
从概念上讲,它和你写的一样
所以当你写
它可以(再次从概念上)翻译成
这意味着当时
执行时,它指向
Rectangle.prototype.constructor()
Square 实例上下文。因此最终结果this.name
为"Rectangle"
。