2014-05-16 149 views
1

目前我們已經構建了自己的Javascript框架,用於爲我們複雜的Web應用程序構建小部件,div,面板和表單。我們所有的小部件(aka.Component)都繼承了一個名爲可視的的超級對象,它主要定義了一個視圖,該視圖是一個HTMLElememntJavaScript繼承最佳策略

Viewable = { 
    getView: function() { 
     if (!this.view) 
      this.view = this.createView(); 
     return this.view; 
    } 
    createView: function() { 
     alert(‘You have not implemented createView. You are a very naughty boy!); 
    } 
} 

然後我們使用的Object.create(可視範圍)創造我們自己的組件,這些組件都需要實現CreateView的在可見的定義。

Header = Object.create(Viewable); 
Header.createView = function() { 
    var div = document.createElement('div'); 
    div.id = 'Header'; 
} 

Header.foobar = function() { 
} 

我想離開這種類型的繼承,這個創建對象在飛,並根據我的心情只是添加方法。

我看了這個其他的方法,使用$ .extends從jQuery。然後,我可以創造我的對象(或者不如說「定義我的函數」「定義我的課」?)

function Header() { 

    $.extend(true, this, Viewable); 

    this.createView = function() { 
     var div = document.createElement('div'); 
     div.id = 'Header'; 
    } 

    this.foobar = function() { 
    } 
} 

我想修改我的代碼,這第二種方法,因爲對我來說的好處是:

  • 你得到一個構造
  • 出現了一定程度的封裝
  • 它類似於面向對象的語言如Java(我來自這個世界..)
  • 我的IDE(Webstorm)喜歡在這更好告訴和重構。

但我不確定。有什麼缺點嗎?我將不得不重構50多個文件,所以我對這樣做有點緊張。我還是比較新的Javascript。

此外,當我們在這裏,一個快速的子問題。如果我重構我可視看起來像這樣:

function Viewable() { 
    this.getView = function() { 
     if (!this.view) 
      this.view = this.createView(); 
     return this.view; 
    }, 
    createView:function() { 
     alert(‘You have not implemented createView. You are a very naughty boy!); 
    } 
} 

這將是更有益?我喜歡它,因爲它使代碼看起來一致。

回答

1

JavaScript沒有類定義(尚),沒有接口。 根據我的心情添加方法是原型繼承的工作原理。

使用Object.create$.extend不會改變這一點。

請記住,$.extend不會給你一個繼承樹。 Header instanceof Viewablefalse,您只需複製Viewable上定義的屬性即可。 $.extend就像是修改輸入參數的對象合併。

如果instanceof對你不重要,那麼你可以使用任何一種方法。

對於如何在JavaScript中獲得類定義,ES6有一些很好的新想法。 ES6還沒有實現,但如果你想要一個可行的預覽,你可以看看Microsoft's TypeScript。TypeScript與ES6不是100%兼容,但它在兼容的地方很重要。

TypeScript增加了類和接口和所有爵士樂。這給你你想要的類型提示,但在編譯回JavaScript時被刪除。

+0

instanceof和多態性尚不重要。我們只是想在這個階段重用通用代碼:) –

1

至少兩個不同和有用的策略實現類類型的代碼結構。一個是原型的,另一個是工廠式的繼承。

原型繼承

JavaScript的原型繼承是經典的和有效的。

// A viewable device. 
function Device() { 
    var _element = null, 
    _type = 'div'; 

    // Get or set the element type. 
    this.type = function type(type_) { 
    if (!arguments.length) 
     return _type; 
    _type = type_; 
    return this; 
    }; 

    // Lazy creation of the element, or set it explicitly. 
    this.element = function element(element_) { 
    if (!arguments.length) 
     return _element || (_element = $('<' + _type + '>').get(0)); 
    _element = element_; 
    return this; 
    }; 

    // Allow constructor chaining on subclasses. 
    return this; 
} 

Device.prototype = Object.create(null); 
Device.prototype.constructor = Device; 

// Get/set. Hide or show this device. 
Device.prototype.visible = function visible(show) { 
    if (!arguments.length) 
    return $(this.element()).css('display') !== 'none'; 
    $(this.element()).css('display', show ? '' : 'none'); 
    return this; 
}; 

// Add or remove a css class, or check for its presence. 
Device.prototype.classed = function classed(css_class, classed_) { 
    if(arguments.length === 1) 
    return $(this.element()).hasClass(css_class); 
    if (classed_) 
    $(this.element()).addClass(css_class); 
    else 
    $(this.element()).removeClass(css_class); 
    return this; 
}; 

雖然Device是一個基類,它可以被實例化,並且被配置如下:

// Create a list item device. 
var ul = new Device() 
    .type('ul') 
    .classed('list-items', true) 
    .visible(false); 

// Check for the class. 
ul.classed('list-items'); // => true 

// Is the device visible? 
ul.visible() // => false 

// Show the device. 
ul.visible(true); 
ul.visible(); // => true 

爲了使list-items設備的子類:

function ListItems() { 
    Device.call(this) 
    .classed('list-items', true) 
    .visible(false); 
    return this; 
} 

ListItems.prototype = Object.create(Device.prototype); 
ListItems.prototype.constructor = ListItems; 

ListItems.prototype.addItem = function addItem(content, css_class) { 
    $(this.element()).append($('<li>') 
    .addClass(css_class || 'list-item') 
    .html(content)); 
    return this; 
}; 

來實例化亞類:

var ul = new ListItems() 
    .addItem('Item 1') 
    .addItem('Item 2') 
    .addItem('Item 3') 
    .visible(true); 

ul.element(); 

/* 
<ul class="list-items"> 
    <li class="list-item">Item 1</li> 
    <li class="list-item">Item 2</li> 
    <li class="list-item">Item 3</li> 
</ul> 
*/ 

廠繼承

廠繼承優雅,並消除了對new關鍵字的需求,如果它是麻煩。

function device() { 
    var self = {}, 
    _type = 'div', 
    _element = null; 

    self.type = function type(type_) { 
    if (!arguments.length) 
     return _type; 
    _type = type_; 
    return this; 
    }; 

    self.element = function element(element_) { 
    if (!arguments.length) 
     return _element || (_element = $('<' + _type + '>').get(0)); 
    _element = element_; 
    return this; 
    }; 

    self.visible = function visible(show) { 
    if (!arguments.length) 
     return $(this.element()).css('display') !== 'none'; 
    $(this.element()).css('display', show ? '' : 'none'); 
    return this; 
    }; 

    self.classed = function classed(css_class, classed_) { 
    if(arguments.length === 1) 
     return $(this.element()).hasClass(css_class); 
    if (classed_) 
     $(this.element()).addClass(css_class); 
    else 
     $(this.element()).removeClass(css_class); 
     return this; 
    }; 

    return self; 
} 

要實例設備:

var ul = device() 
    .type('ul') 
    .classed('list-items', true) 
    .visible(false); 

從設備繼承:

function listItems() { 
    var _super = device() 
    .type('ul') 
    .classed('list-items', true) 
    .visible(false), 
    self = Object.create(_super); 

    self.addItem = function addItem(content, css_class) { 
    $(this.element()).append($('<li>') 
     .addClass(css_class || 'list-item') 
     .html(content); 
    return this; 
    }; 

    return self; 
} 

實例化時listItems:

var ul = listItems() 
    .addItem('Item 1') 
    .addItem('Item 2') 
    .addItem('Item 3') 
    .visible(true); 

ul.element(); 

/* 
<ul class="list-items"> 
    <li class="list-item">Item 1</li> 
    <li class="list-item">Item 2</li> 
    <li class="list-item">Item 3</li> 
</ul> 
*/ 

使用哪種模式在很大程度上是一個事的偏好,雖然親totypal繼承類在調試器中具有不同的身份,因此可能更適合調試和分析情況。

原型繼承與instanceof一起工作,所以這是另一個考慮因素。一個instanceOf類型的機制可以添加到工廠繼承的一些努力。

1

我在我們的一個基於java腳本的項目中有類似的需求。它是Java實現的JS端口,一個要求是代碼結構,邏輯應該與Java相似。這是我想出的。

聲明:我對Java Script有點新。以下解決方案基於我的理解爲基礎的 迄今爲止的語言,我希望它是 在公認的指南和最佳實踐。

下面的代碼演示瞭如何在Java中'擴展'關鍵詞可以模仿在JS中。

Function.prototype.extends = function (parent) { 
    var child = this; 
    child.prototype.inherit = function() { 
    var parentProto = parent; 
    var childProto = child; 
    var parentObj = new parentProto(); 
    childProto.prototype = parentObj; 
    childProto.prototype.$super = parentObj; 
    var newObj; 
    if(child.arguments.length >0) newObj = new childProto(child.arguments[0]); 
    else newObj = new childProto(); 

    /*Restore the inherit function back in type prototype so that subsequent 
    creation of unique objects are possible*/ 
    if(typeof this == "function") childProto.prototype.inherit = this; 
    return newObj; 
    }; 
} ; 

這裏是代碼的其餘部分。

//Class A 
var A = function(){ 
//some field 
//some methods 
} 

//Class B 
var B = function(){ 

//Check if inheritance needed 
    if(this.inherit){ 
    var newInst = this.inherit.call(this.inherit); 
    return newInst; 
    } 
//rest of class B specific code 
} 

//Here goes the inheritance statement like "Class B extends A" 
B.extends(A); 

到目前爲止,這對我有用,只用於1級繼承。 希望這有助於。