2014-08-27 52 views
-1

我想創建一個使用javascript原型繼承的網格,但有一個問題,我不明白。在代碼的某些部分,「新」關鍵字似乎不起作用。在Javascript Instanciation意外的結果

這很難解釋,所以代碼如下,我把意見放在要點上。

<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文檔中,您將會看到問題。黃色矩形應該在紅色矩形內!

任何人都知道會發生什麼?

回答

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()); 
    } 
} 
+0

謝謝!現在我知道如何使用Object.create進行繼承了!我已經嘗試過但沒有成功。謝謝你的回答,它解決了我的問題。我也會看到關於ES6的。我不知道它已經可用了!謝謝:) – jairhumberto 2014-08-27 15:21:34

+1

我認爲任何瀏覽器都不支持類語法,但有些轉換工具會將其轉換爲有效的ES5代碼。例如http://www.phpied.com/writing-es6-today-with-jstransform/ – 2014-08-27 15:22:37

1

的問題不在於new關鍵字不工作了,而是它位於以下行: GridPart.prototype = new Container('grd-part');

它做什麼是cau全部GridPart對象具有相同的this.components對象。

所以,當你調用this.addComponentGridPart構造函數裏面,你在指數'comp'用新Container對象

我已經找到一種方法來實現你正在尋找here 和綜合繼承覆蓋相同this.components對象這個解決方案進入你的代碼

這裏是工作codepen