javascript - 原型链

javascript - prototype chain

请考虑这样的代码:

class a {
    constructor() {
        this.test = "test from a";
        this.testA = "X";
    }

    mimi() {
        console.log('aaaaaaaa', this.test, this.testA)
    }

}

class b extends a {
    constructor() {
        super();
        this.test = "test from b"
    }

    mimi() {
        console.log('bbbbbbbb', this.test, this.testA)
    }

}

class c extends b {
    constructor() {
        super();
        this.test = "test from c"
    }

    mimi() {
        console.log('cccccccc', this.test, this.testA)
    }

    meme() {
        var test = kalreg.__proto__.__proto__;
        test.mimi();
        var test2 = kalreg.__proto__.__proto__.__proto__;
        test2.mimi();
    }
}

var kalreg = new c(); kalreg.mimi(); kalreg.meme();

我得到的输出是:

cccccccc test from c X
bbbbbbbb undefined undefined
aaaaaaaa undefined undefined

我的对象逻辑让我使用“a”作为最通用的class,“b”是一个它的子项,而“c”是“b”的子项。我希望“c”具有“b”和"a"的所有方法和属性,但“[=32]的部分功能=]a”被“b”覆盖,所以“c”访问被覆盖功能的唯一方法是使用原型链。 不幸的是,我的方法行不通,所以问题是:

  1. meme() 函数中,如何避免 kalreg.proto - 有人告诉我这种访问原型的方式对代码来说很糟糕而且很危险。
  2. 在我看来,输出中不应该有"undefined",但是,有。 预期输出为:

    来自 c X 的 cccccccc 测试

    bbbbbbbb 测试来自 b X

    aaaaaaaa 来自 X

  3. 的测试

如何实现?

谢谢!

in meme() function, how to avoid kalreg.proto

在确实需要从超类访问 属性 的用例中使用 super(但请注意,这对 test 和 [=14 没有帮助=],因为它们不在超类或其 prototype 对象上,它们在使用 new c 创建的实例上;更多内容见下文)。

另外:避免__proto__。在需要访问对象原型的极少数情况下,请使用 Object.getPrototypeOf.

in my opinion there should be no "undefined" in output however there is. expected output is:

cccccccc test from c X

bbbbbbbb test from b X

aaaaaaaa test from a X

输出是正确的,因为您调用 mimi 的原型对象没有 testtestA 属性。只有使用 new c 创建的对象才具有这些属性。而且只有一个这样的对象,所以 test 将是 "test from c" 无论你调用哪个 mimi,而 testA 将永远是 "X".

在评论中,您问过当每个构造函数中都有 this.test = ... 时,怎么可能只有一个 test。完成后让我们看看内存中的内容 var kalreg = new c();:

                                         +−−−−−−−−−−−−+
a−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−>| (function) |  +−−−>(Function.prototype)
                                      |  +−−−−−−−−−−−−+  |
                                      |  | __proto__  |−−+          +−−−−−−−−−−−−−+
                                      |  | prototype  |−−−−−−−−−−+−>|  (object)   |
                                      |  +−−−−−−−−−−−−+          |  +−−−−−−−−−−−−−+
                                      |                          |  | __proto__   |−−>...
                       +−−−−−−−−−−−−+ |                          |  | constructor |−−>a
b−−−−−−−−−−−−−−−−−−−+−>| (function) | |                          |  | mimi        |−−>...
                    |  +−−−−−−−−−−−−+ |                          |  +−−−−−−−−−−−−−+
                    |  | __proto__  |−+         +−−−−−−−−−−−−−+  |
                    |  | prototype  |−−−−−−−−+−>|  (object)   |  |
                    |  +−−−−−−−−−−−−+        |  +−−−−−−−−−−−−−+  |
                    |                        |  | __proto__   |−−+
     +−−−−−−−−−−−−+ |                        |  | constructor |−−>b
c−−−>| (function) | |                        |  | mimi        |−−>...
     +−−−−−−−−−−−−+ |                        |  +−−−−−−−−−−−−−+
     | __proto__  |−+        +−−−−−−−−−−−−−+ |
     | prototype  |−−−−−−−+−>|  (object)   | |
     +−−−−−−−−−−−−+       |  +−−−−−−−−−−−−−+ |
                          |  | __proto__   |−+
                          |  | constructor |−−>c
          +−−−−−−−−−−−−+  |  | mimi        |−−>...
kalreg−−−>|  (object)  |  |  | meme        |−−>...
          +−−−−−−−−−−−−+  |  +−−−−−−−−−−−−−+
          | __proto__  |−−+
          | test       |
          | testA      |
          +−−−−−−−−−−−−+

如你所见,只有kalreg指向的对象有testtestA。为什么?因为在调用每个构造函数期间,this 指的是那个对象;这就是 newsuper() 的工作原理。因此,由于 this 引用该对象,每个构造函数都执行其 this.test = ... 行,并且由于 c 中的那个是 运行 的最后一个,它“获胜”。 =57=]

您可以访问 mimi 方法的超类版本,但由于它们都显示 test 属性,因此它们都会显示 "test from c"。要显示不同的内容,它们必须显示 不同的 属性。此外,使用 super 你只能向上一级,所以如果你想向上两级,你要么使用 a.prototype.mimi (或明确地使用 this.__proto__.__proto__.mimi ,要么在b 调用 amimi.

每个级别具有不同属性的示例,并且 b 提供 superMimi 因此 c 可以使用 amimi:

class a {
    constructor() {
        this.testA = "test from a (this.testA)";
    }

    mimi() {
        console.log('aaaaaaaa', this.testA);
    }
}

class b extends a {
    constructor() {
        super();
        this.testB = "test from b (this.testB)";
    }

    mimi() {
        console.log('bbbbbbbb', this.testB)
    }
    
    superMimi() {
      return super.mimi();
    }
}

class c extends b {
    constructor() {
        super();
        this.testC = "test from c (this.testC)";
    }

    mimi() {
        console.log('cccccccc', this.testC);
    }

    meme() {
        super.mimi();      // Uses b's version
        super.superMimi(); // Uses a's version
        this.__proto__.__proto__.__proto__.mimi.call(this); // Also uses a's version
        var p = Object.getPrototypeOf(this);
        var p2 = Object.getPrototypeOf(p);
        var p3 = Object.getPrototypeOf(p2);
        p3.mimi.call(this); // Also uses a's version
    }
}

var kalreg = new c();
kalreg.mimi();
kalreg.meme();