用默认值扩展js中的行为



我发现了js扩展的有趣行为,并且不理解它的原因
在从另一个值复制值的情况下,由于某些原因值将从父

复制
class parent {
defaultValue = 1;
value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
defaultValue = 2;
}
new child() // {defaultValue: 2, value: 1}

这对我来说真的不是很明显和不清楚
但是如果我用function甚至getter来代替它行为将会改变,我从child

获得值
class parent {
get defaultValue() { return 1; }
value = this.defaultValue;
}
new parent() // {defaultValue: 1, value: 1}
class child extends parent {
get defaultValue() { return 2; }
}
new child() // {defaultValue: 2, value: 2}

这里的主要问题是,为什么在第一种情况下,JS在父类中寻找值,但在第二种情况下,JS在子类中寻找值

有人能解释一下这种行为的原因吗?编辑详情请参阅t.t niese或Yury Tarabanko的回答

简短的答案似乎在下面

getter(也是function)和function将在原型中被重写,从而允许父类在子类更改时调用它们(实际上这是预期的)

而第一个带有简单赋值的示例只会在类创建时(构造函数或父类)被调用,并且它只会出现在当前类(不能被子类更改)和原型(可以被子类更改)的作用域

一个相关的问题是:如何在父类代码中访问被重写的父类函数

getter和setter是与类定义一起定义的函数,因此在parent类的构造函数(以及它的实例类字段的初始化)中,您可以调用只存在于child中的函数(这确实可能有点奇怪):

class parent {
value = this.test();
constructor() {
this.test()
}
}
class child extends parent {
test() {
console.log('test')
}
}
new child()

因此,在实例化完成之前,调用的函数(或getter/setter)已经用类定义定义好了。

另一方面,公共实例类字段在实例初始化阶段以特定顺序初始化/设置(所示代码可能仅适用于基于chrome的浏览器):

class parent {
defaultValue = (() => {
console.log('parent:init defaultValue')
return 1;
})();
value = (() => {
console.log('parent:init value')
return this.defaultValue;
})();
constructor() {
console.log('parent constructor')
}
}
class child extends parent {
defaultValue = (() => {
console.log('child:init defaultValue')
return 2;
})();

constructor() {
console.log('child constructor before super()')
super()
console.log('child constructor after super()')
}
}
new child()

在第一个示例中,Child中名为defaultValue的公共实例字段的创建和初始化发生在Parent中名为value的公共实例字段的创建和初始化之后。

因此:即使Parent中名为value的公共实例字段的初始化器中的this值指向正在构建的Child的实例,但名为defaultValue的子本地公共实例字段还不存在,因此原型链接着到Parent实例上名为defaultValue的属性,产生1.

在后面的示例中,您有名为defaultValue的getter函数。

以这种方式指定的Getter函数,即使它们的API故意看起来像公共实例字段,最终也会成为任何正在构建的实例的[[Prototype]]上的函数。

实例的[[Prototype]]对象是在类声明时创建的。总是在由实例构造触发的任何事情之前),作为类(或构造函数)的.prototype属性;然后将对这些对象的引用复制到任何正在构建的实例的[[Prototype]]中,作为对象构建的第一步(考虑Object.create(class.prototype)).

)。因此,valueParent公共实例初始化器中的this.defaultValue解析为Child正在构造的实例的[[Prototype]]上的getter,即返回2的函数。.

这是因为getter是在原型上定义的,而实例属性是在实例上定义的(顾名思义)

创建Child1实例时它首先从Parent1中定义属性然后得到defaultValue = 1

相反,当创建Child2实例时,Child2.prototype已经覆盖了defaultValue属性。

class Parent1 {
defaultValue = 1;
value = this.defaultValue;
}
class Child1 extends Parent1 {
defaultValue = 2;
}


class Parent2 {
get defaultValue() { return 1; }
value = this.defaultValue;
}

class Child2 extends Parent2 {
get defaultValue() { return 2; }
}

console.log(Object.hasOwn(new Child1(), 'defaultValue'))
console.log(Object.hasOwn(new Child2(), 'defaultValue'))

最新更新