Javascript继承最佳策略

Javascript Inheritance Best Strategy

本文关键字:策略 最佳 继承 Javascript      更新时间:2023-09-26

目前我们已经构建了自己的Javascript框架,用于为我们复杂的web应用程序构建小部件、div、面板和表单。我们所有的小部件(又名。组件)继承自一个名为Viewable的超对象,它主要定义了一个视图,该视图是 htmlelement

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在Viewable中定义。

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

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

我已经使用$研究了另一种方法。从jQuery中扩展。然后我可以创建我的对象(或者更好地说"定义我的函数"?'定义我的类' ?)
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还是个新手。

同时,一个快速的子问题。如果我把可视重构成这样:

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

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

JavaScript还没有类定义,没有接口。添加方法取决于我的心情是原型继承的工作原理。

使用Object.create$.extend不会改变这个

请记住,$.extend并没有给你一个继承树。Header instanceof Viewablefalse,你只是复制在Viewable上定义的属性。$.extend类似于修改输入参数的对象合并。

如果instanceof对你来说不重要,那么你可以使用任何一种方法。

ES6在如何在JavaScript中获得类定义方面有一些很好的新想法。ES6还没有实现,但是如果你想要一个可行的预览,你可以看看微软的TypeScript。TypeScript与ES6并不是100%兼容,但它在重要的地方是兼容的。

TypeScript添加了类和接口等等。这为您提供了所需的类型提示,但在编译回JavaScript时将被删除。

实现类类型代码结构至少有两种不同且有用的策略。一个是原型继承,另一个是工厂风格继承。

原型继承

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

从device继承:

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

实例化listtitem:

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类型的机制可以通过一些努力添加到工厂继承中。

我在一个基于java脚本的项目中也有类似的需求。它是Java实现的JS移植,其中一个要求是代码结构和逻辑应该与Java一样相似。这是我想出来的。

免责声明:我对Java Script有点陌生。下面的解决方案是基于就我目前对语言的理解,我希望是这样在公认的指导方针和最佳实践范围内。

下面的代码演示了如何在JS中模仿Java中的"extends"关键字。

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级继承。