【问题标题】:Unexpected values shared between prototypes原型之间共享的意外值
【发布时间】:2014-10-12 14:04:47
【问题描述】:

我不是 Javascript 专家,但我遇到了一个非常奇怪的情况,我无法解释。 我想看看你们中的一些人是否可以帮助我理解这一点:-)

首先,让我们看看工作正常的部分:

我想设置三个类,一个是其他类的基类,像这样:

Base
 |- Atom
 |- Button

这是我使用的代码:

//-------------------------------------------------------------------

function Base() {
    this.values = {
        a: true,
        b: false
    };
}

Base.prototype.display = function() {
    for(i in this.values) {
        window.console.log(i + ' : ' + this.values[i]);
    }
    window.console.log('---');
}

//-------------------------------------------------------------------

function Atom() {
    this.values.a = false;
    this.values.c = 'test';
}

Atom.prototype = new Base();

//-------------------------------------------------------------------

function Button() {
    this.display();                  // displays part (1)

    this.atom = new Atom();

    this.display();                  // displays part (2)
    this.atom.display();             // displays part (3)
}

Button.prototype = new Base();

//-------------------------------------------------------------------

new Button();

“Base”类有一个名为“values”的属性,它是值的集合。 "display" 方法只是将 "values" 属性显示到控制台。

“Atom”类从“Base”继承“values”属性,它只是改变了 “a”的值并添加一个新的“c”。

“Button”类有一个属性,它是 Atom 类的一个对象。 它在创建其“原子”之前和之后调用其“显示”方法 属性,那么它也会为“atom”本身调用“display”方法。

到目前为止,一切都很好。这是屏幕上的结果:

a : true                // part (1)
b : false
---
a : true                // part (2)
b : false
---
a : false               // part (3)
b : false
c : test
---

前两组值 (1) 和 (2) 来自 Button 类,第三组 (3) 来自 Atom 类,修改了“a”并添加了“c”。

当我想在“Base”和它的中间类之间添加一个中间类时,问题就来了 后代,像这样:

Base
 |- Widget
     |- Atom
     |- Button

下面是新“Widget”类的代码:

//-------------------------------------------------------------------

function Base() {
    this.values = {
        a: true,
        b: false
    };
}

Base.prototype.display = function() {
    for(i in this.values) {
        window.console.log(i + ' : ' + this.values[i]);
    }
    window.console.log('---');
}

//-------------------------------------------------------------------

function Widget() {
}

Widget.prototype = new Base();

//-------------------------------------------------------------------

function Atom() {
    this.values.a = false;
    this.values.c = 'test';
}

Atom.prototype = new Widget();

//-------------------------------------------------------------------

function Button() {
    this.display();                  // displays part (1)

    this.atom = new Atom();

    this.display();                  // displays part (2)
    this.atom.display();             // displays part (3)
}

Button.prototype = new Widget();

//-------------------------------------------------------------------

new Button();

如您所见,“Atom”和“Button”类的基本原型具有 已更改为“Widget”而不是“Base”。

这就是事情变得奇怪的地方:

a : true                // part (1)
b : false
---
a : false               // part (2)
b : false
c : test
---
a : false               // part (3)
b : false
c : test
---

第一组(在“原子”创建之前)没有改变。 但是按钮的值将在“原子”创建后立即更改, 它们现在与“原子”的相同,这不是我所期望的。

这就是我不明白的原因。 创建原子时,它会修改自己的一组值,但是为什么 按钮的值是否也被修改了?

我认为“Button.values”和“Button.atom.values”是两个不同的东西, 但看起来他们分享他们的价值观,但他们只有在中间人的情况下才会这样做 “Widget”类是“Button”和“Atom”的父类。

这让我很困惑... 我希望我的解释很清楚,如果不是这样,请随时告诉我。

非常感谢您的时间和回答:-) 马克。

【问题讨论】:

标签: javascript prototype prototype-programming


【解决方案1】:

values 指的是一个对象,并且由于您实现伪经典继承的方式,只有 一个 分配给该属性的对象最终在原型之间共享。您的 WidgetAtom 最终都使用 same 对象。

在使用 JavaScript 设置伪经典继承时,您会看到您经常使用的模式,但这很不幸,因为它存在重大问题(包括这个问题)。具体来说,调用Base 来创建原型Atom(等等)是有问题的,因为一般情况下,像Base 这样的构造函数是设计用于构造实例时使用的。我通常向他们提出的问题是:如果Base 需要接受特定于实例的参数怎么办?创建Atom 原型时,您会给出什么论据?

相反,在构造实例之前不要调用构造函数。要构造一个原型,请创建一个使用父“类”的原型作为其底层原型的新对象,如下所示:

function Parent() {
}

function Child() {
    Parent.call(this);
    // Child initialization goes here
}

Child.prototype = Object.create(Parent.prototype); // Not `new Parent()`
Child.prototype.constructor = Child;

(如有必要,您可以为 ES5 之前的引擎填充 Object.create 的单参数版本。)

可能会解决您的问题的原因是,使用此模式,Base 函数将在创建每个实例时被调用,这意味着它会创建一个新对象并将其分配给该实例的 values 属性。

旁注:为了节省冗长并获得一些功能,我创建了一个名为 Lineage 的小帮助脚本,用于设置伪经典层次结构。它简化了语法,使调用超“类”版本的方法变得容易,以及其他各种事情。

【讨论】:

  • 哇,非常感谢您的回答 :-) 我正在使用 T.J.克劳德的代码,到目前为止还可以:-)
  • 但现在我必须找到一种方法来执行 IE8 不支持的 Object.create... :'(
  • 我在 Mozilla 上找到了它,现在它也适用于 IE8 (developer.mozilla.org/fr/docs/Web/JavaScript/Reference/…)。再次感谢:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 2021-09-04
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多