Javascript继承最佳策略

时间:2014-05-16 12:52:55

标签: javascript jquery inheritance refactoring

目前,我们已经构建了自己的Javascript框架,用于为复杂的Web应用程序构建窗口小部件,div,面板和表单。我们所有的小部件(也称为组件)都继承自名为可查看的超级对象,该对象主要定义一个 HTMLElememnt 的视图。

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(可查看)来创建我们自己的组件,这些组件都需要实现在Viewable中定义的 createView

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

Header.foobar = function() {
}

我想摆脱这种类型的继承,即动态创建对象,并根据我的心情添加方法。

我已经从jQuery使用 $ .extended 查看了另一种方法。然后我可以创建我的对象(或者更好地说'定义我的函数'?'定义我的类'?)

function Header() {

    $.extend(true, this, Viewable);

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

    this.foobar = function() {
    }
}

我想将我的代码重构为第二种方法,因为对我而言,好处是:

  • 你得到一个构造函数
  • 有一定程度的封装
  • 它类似于Java这样的OO语言(我来自这个世界......)
  • 我的IDE(Webstorm)更喜欢智能感知和重构。

但我不确定。有什么缺点吗?我将不得不重构50多个文件,所以我有点担心这样做。我仍然是Javascript的新手。

同样在我们处理它时,一个快速的子问题。如果我将 Viewable 重构为如下所示:

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!);
    }
}

这会更有益吗?我喜欢它,因为对我来说它使代码看起来一致。

3 个答案:

答案 0 :(得分:1)

JavaScript没有类定义(还没有),没有接口。 根据我的心情添加方法是原型继承的工作原理。

使用Object.create$.extend不会改变这种情况。

请记住,$.extend不会为您提供继承树。 Header instanceof Viewablefalse,您只是复制Viewable上定义的属性。 $.extend就像一个修改输入参数的对象合并。

如果instanceof对您不重要,那么您可以使用其中任何一种方法。

ES6对如何在JavaScript中获取类定义有一些很好的新想法。 ES6尚未实现,但如果您想要可行的预览,可以查看Microsoft's TypeScript。 TypeScript与ES6不是100%兼容,但在重要的地方它是兼容的。

TypeScript添加了类和接口以及所有爵士乐。这为您提供了所需的类型提示,但在编译回JavaScript时会被删除。

答案 1 :(得分: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>
*/

使用哪种模式主要是偏好问题,尽管原型继承类在调试器中具有不同的标识,因此可能更适合调试和分析情况。

原型继承也适用于instanceof,这是另一个考虑因素。可以通过一些努力将instanceOf - 类型机制添加到工厂继承中。

答案 2 :(得分:1)

我在一个基于java脚本的项目中有类似的需求。它是Java实现的JS端口,一个要求是代码结构,逻辑应该与Java类似。这就是我想出的。

  

免责声明:我对Java Script有点新鲜。以下解决方案基于   根据我的理解,我到目前为止对语言有所了解,我希望它是   在公认的指南和最佳实践中。

以下代码演示了&#39;扩展&#39; 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级继承。 希望这会有所帮助。