【问题标题】:Unexpected Result in Javascript InstanciationJavascript 实例化中的意外结果
【发布时间】:2014-08-27 14:38:21
【问题描述】:

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

很难解释,所以代码如下,我把cmets放在了重点。

<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 文档中,您将看到问题。黄色矩形应该在红色矩形内!

有人知道会发生什么吗?

【问题讨论】:

    标签: javascript inheritance


    【解决方案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());
        }
    }
    

    【讨论】:

    • 谢谢!现在我知道如何使用 Object.create 进行继承了!我以前尝试过,但没有成功。谢谢你的回答,它解决了我的问题。我也会看到 ES6。我不知道它已经可用了!谢谢你:)
    • 我不认为任何浏览器都支持类语法,但是有一些转换工具可以将其转换为有效的 ES5 代码。例如phpied.com/writing-es6-today-with-jstransform
    【解决方案2】:

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

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

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

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

    这是一个有效的codepen

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多