gpt4 book ai didi

javascript - 子扩展类方法调用其 super 版本,但仍然只能看到子数据

转载 作者:行者123 更新时间:2023-11-30 09:15:06 27 4
gpt4 key购买 nike

B 类扩展了 A 类。我称 A 为父类,B 为子类。两者都有构造函数。 B 在其构造函数内部调用 super() 。两者都有一个同名的方法。也许只是巧合或错误,两者都有一个“this.x”变量。然后就无法访问父级的 this.x 变量。然后它成为 child 和 parent 之间可能无意识的交流点。

   class A {
constructor(){
this.x = "super x!";
}
logx(){
console.log(this.x);
}
}

class B extends A{
constructor(){
super();
this.x = "derived x.";
}
logx(){
super.logx();
}
}

let b = new B;
b.logx(); // expected "super x!", but it prints "derived x".

A 类可能来自图书馆,或者是由其他人编写的。甚至可能是 A 类的作者来编辑代码并添加一个新变量,然后为他甚至不知道存在的 child 取别名。子类的作者必须成为父类变化的狂热读者,这样他或她就可以相应地更新他或她自己的代码,如果这个作者确实还在这个项目中的话。 (正是这样一个错误导致我今天来到这里,这是它的升华。)

在下面的代码中,我通过给每个变量一个与类名相同的前缀来防止这个问题。然后我得到预期的行为。当然有更好的方法。也许其中一些私有(private)/公共(public)关键字会有所帮助?

      constructor(){
this.A_x = "super x!";
}
logx(){
console.log(this.A_x);
}
}

class B extends A{
constructor(){
super();
this.B_x = "derived x.";
}
logx(){
super.logx();
}
}

let b = new B;
b.logx(); // expected "super x!", and indeed it prints "super x!"

这也发生在方法调用中,尽管这并不令人惊讶,因为 a) 被认为是“多态性” b) 上游代码接口(interface)的更改通常会对下游代码产生影响。然而,程序员可能有一些不打算在接口(interface)上的辅助函数,如果子类作者碰巧想到了相同的辅助函数名称,或者使用该名称的函数扩展了接口(interface)......

   class A {
constructor(){
this.x = "super x!";
}
f(){
console.log("I am a super f()!");
}
logx(){
this.f(); // aliased - polymorphism behavior
console.log(this.x);
}
}

class B extends A{
constructor(){
super();
this.x = "derived x.";
}
f(){
console.log("I am a derived f()");
}
logx(){
super.logx();
}
}

let b = new B;
b.logx();

控制台输出:

I am derived f()
derived x.

根据 Jonas Wilms 对正在发生的事情展开的评论,确实可以使用组合模式来封装父级数据,从而防止意外混叠:

   class A {
constructor(){
this.x = "super x!";
}
f(){
console.log("I am a super f()!");
}
logx(){
this.f();
console.log(this.x);
}
}

class B {
constructor(){
this.a = new A();
this.x = "derived x.";
}
f(){
console.log("I am a derived f()");
}
logx(){
this.a.logx();
}
}

let b = new B;
b.logx();

它的行为符合预期,控制台输出:

    I am a super f()!
super x!

然而,这并非没有问题。首先,instanceof 运算符不起作用。其次,我们不继承任何方法。子类的作者将不得不添加只接受参数并将它们传递给父类方法的 stub 。这可能会影响性能。参见 ES6 et al. is it possible to define a catch-all method? .

.. 这个问题似乎可以归结为“您如何定义界面上有什么,什么没有?”哎呀,有一个演示为什么有人可能喜欢这样做。

最佳答案

实际上你的层次结构等于

 // a constructor is just a function
function A() {
this.x = "super x!";
}

A.prototype.logx = function() { console.log(this.x); };

function B() {
A.call(this); // "this" gets passed, no new instance gets created
this.x = "derived x";
}

B.prototype = Object.create(A.prototype); // extending a class basically lets the prototype of the class inherit the prototype of the superclass
B.prototype.constructor = B;

B.prototype.logx = function() {
A.prototype.logx.call(this); // we can reference A#logx as it exists on the prototype
};

// using "new" basically creates a new object inheriting the prototype, then executes the constructor on it
let b = Object.create(B.prototype);
B.call(b);

所以虽然实际上有两个 logx 方法可以引用(一个在 A 的原型(prototype)上,一个在 B 的原型(prototype)上),但只有一个实例 (this) 可以获取在构造期间传递,并且设置对象的属性会覆盖以前的值。因此你是对的,没有办法让不同的属性具有相同的名称。

Gosh hope one doesn't need to do something such as adopt a convention of giving every variable a prefix based on its class name if one wants to assure that parent variables remain independent

我真的建议使用 Typescript 来关注结构(有一个 privatereadonly 属性修饰符)。在 JS 中,您可以使用 Symbols 来模仿私有(private)属性:¹

 class A {
constructor() {
this[A.x] = "stuff";
}
}

A.x = Symbol();

class B extends A {
constructor() {
this[B.x] = "other stuff";
}
}

B.x = Symbol();

console.log(new B()[A.x]);

(当然,您可以将 Symbols 保留在任何类型的变量中,无需使其成为类的一部分)。

或者你只是放弃继承的东西, B 和 A 组合起来:

 class B {
constructor() {
this.a = new A();
this.x = "new x";
}
}

(new B).x
(new B).a.x

Will this also happen for method calls?

是的,因为 B 实例的继承链是:

 b -> B.prototype -> A.prototype

方法将首先在 b 中查找,然后在 B 中查找,最后在 A 中查找,因此如果有一个方法带有在 A 和 B 中都命名为“logx”,将采用 B 中的一个。你也可以这样做:

 b.logx = function() { console.log("I'm first!");

so, what does one do when writing parent code, if one wants the parent f()?

你可以直接在原型(prototype)上调用它:

 A.prototype.logx.call(b /*...arguments*/);

在一个方法中,您可以采用this 而不是具体实例(本例中为b)。如果您不想采用特定的实现,而是采用 super 类之一,请像您一样使用 super.logx()


¹ 老实说:我从来没有遇到过任何问题,只要您正确命名您的属性,名称就很少会发生冲突。

关于javascript - 子扩展类方法调用其 super 版本,但仍然只能看到子数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55677926/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com