Javascript实例化中的意外结果

时间:2014-08-27 14:38:22

标签: javascript inheritance

我正在尝试使用javascript原型继承创建网格,但是有一个我无法理解的问题。在某些代码中,“new”关键字似乎不起作用。

这很难解释,因此代码如下,我将评论放在要点中。

<head>
    <!-- this is the "parent class" -->
    <script type='text/javascript'>
        // constructor
        function Container(CSSClassName) {
            this.CSSClassName = CSSClassName;
            this.build = ContainerBuild;

            this.components = new Object();

            this.addComponent = ContainerAddComponent;
            this.getComponent = ContainerGetComponent;
        }

        /*
         * methods
         */

        function ContainerAddComponent(id, component) {
            this.components[id] = component;
        }

        function ContainerGetComponent(id) {
            return this.components[id];
        }

        function ContainerBuild() {
            this.element = document.createElement('div');
            this.element.className = this.CSSClassName;

            for (var i in this.components) {
                this.element.appendChild(this.getComponent(i).build());
            }

            return this.element;
        }
    </script>

    <!-- Below I'm using prototype inheritance -->
    <script type='text/javascript'>
        Grid.prototype = new Container('grd');
        function Grid() {
            this.addComponent('body', new GridPart());
            this.addComponent('foot', new GridPart());
        }

        GridPart.prototype = new Container('grd-part');
        function GridPart() {
            this.addComponent('comp', new Container('comp')); // this new keywork seems not to work.

            /* ***** I tried this code, but the result was the same.
            var comp = new Container('comp');
            this.addComponent('comp', Object.create(comp)); // same unexpected result.
            */
        }

        window.onload = function() {
            var grd = new Grid();
            document.getElementById('grd').appendChild(grd.build());

            grd.getComponent('body').element.style.background = 'red'; // ok!
            grd.getComponent('body').getComponent('comp').element.style.background = 'gold'; // unexpected behavior

            // Testing the objects.
            console.log(grd.getComponent('body') === grd.getComponent('foot')); // false, ok!
            console.log(grd.getComponent('body').getComponent('comp') === grd.getComponent('foot').getComponent('comp')); // true?? should be false!
        }
    </script>

    <style type='text/css'>
        .grd { border: 1px solid black }
        .grd-part {
            height: 25px;
            padding: 12px;
            border-bottom: 1px solid black;
        }
        .grd-part:last-child { border:none }
        .grd-part .comp {
            background: black;
            height: 25px;
        }
    </style>
</head>
<body>
    <div id='grd'></div>
</body>

如果您将此代码复制并粘贴到html文档中,您将看到问题所在。黄色矩形应该在红色矩形内!

任何人都知道会发生什么?

2 个答案:

答案 0 :(得分:1)

浏览代码时,我怀疑问题是GridGridPart的每个实例共享相同的components对象。您可以通过查看原型链或检查grd.getComponent('body').components === grd.getComponent('foot').components的结果来验证这一点。

不要做像

这样的事情
Grid.prototype = new Container('grd');

它将特定于实例的属性添加到原型中,因此所有实例共享相同的特定于实例的属性(如components)。相反,使用Object.create来建立继承:

Grid.prototype = Object.create(
  Container.prototype, 
  {constructor: {value: Grid, configurable: true, writable: true}}
);

并在子构造函数中调用父构造函数:

function Grid() {
  Container.call(this, 'grd');
  this.addComponent('body', new GridPart());
  this.addComponent('foot', new GridPart());
}

应该在构造函数中设置特定于实例的属性,在原型上设置应共享的属性。因此,在您的情况下,您应该Container.prototype. ContainerAddComponent = ContainerAddComponent;

另见Benefits of using `Object.create` for inheritance


如果您已经能够使用ES6,请使用新的class语法:

class Grid extends Container {
    constructor() {
      super('grd');
      this.addComponent('body', new GridPart());
      this.addComponent('foot', new GridPart());
    }
}

答案 1 :(得分:1)

问题不在于new关键字不再起作用,而是位于以下行: GridPart.prototype = new Container('grd-part');

它的作用是使所有GridPart个对象具有相同的this.components对象。

因此,当您在this.addComponent构造函数内调用GridPart时,您会使用新的this.components对象覆盖索引'comp'处的同一Container对象

我找到了一种方法来实现您正在寻找的继承here 并将此解决方案集成到您的代码中。

这是一个有效的codepen