原型之间共享的意外值

时间:2014-10-12 14:04:48

标签: javascript prototype prototype-programming

我不是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" class有一个名为"的值"这是一组价值观。 "显示"方法只显示"值"属于控制台。

" Atom" class继承了"值"来自" Base"的财产,它只是改变 " a"的价值并添加一个新的" c"。

"按钮" class有一个属性,它是Atom类的一个对象。 它称之为"显示"创建其原子"之前和之后的方法 属性,然后它也调用"显示" " atom"的方法本身。

到目前为止,这么好。这是屏幕上的结果:

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"的基本原型。和"按钮"班级有 已更改为" Widget"而不是" Base"。

事情变得奇怪的事情:

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

第一组(创建" atom"之前)保持不变。 但是按钮的值将在创建" atom"之后立即更改, 它们现在与" atom"的相同,这不是我的预期。

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

我想" Button.values"和" Button.atom.values"会是两件事, 但看起来他们分享了他们的价值观,但只有中间人才能这样做 "窗口小部件" class是两者的父母" Button"和#34; Atom"。

这对我很困惑...... 我希望我的解释清楚,不要犹豫告诉我,如果不是这样。

非常感谢你的时间和答案:-) 马克。

1 个答案:

答案 0 :(得分:3)

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

使用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的小帮助脚本来设置伪古典层次结构。它简化了语法,调用了超级"类"方法的版本简单,以及其他各种各样的东西。