简单的JavaScript继承

我想萃取有关继承技术的精华到一个简单、可重用、容易理解且不会有任何依赖的形式实现。此外,我也想让这个结果简单并且很是有用。这有一个我想要的效果的例子:
var  Person =  Class . extend ( {
  init:  function ( isDancing ) {
     this . dancing  = isDancing;
   } ,
  dance:  function ( ) {
     return   this . dancing ;
   }
} ) ;

 

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // Call the inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});服务器

var p = new Person(true);
p.dance()// => true闭包

var n = new Ninja();
n.dance()// => false
n.swingSword()// => trueapp

// Should all be true
instanceof Person && p instanceof Class &&
instanceof Ninja && n instanceof Person && n instanceof Class函数

这个实现中有几点须要注意:
一、建立一个构造类必需要简单(这种状况下简单的提供一个init方法就能起做用);
二、要建立一个新的‘class’就必须扩展(sub-class )已经存在的类;
三、全部的 ‘classes’都从一个祖先继承而来:Class。所以若是你想建立一个新类分支,这个新类分支就一定是 Class的子类;
四、最有挑战一点的是:获取被 覆写 了的但 必须被提供 的方法(这些方法的上下文被正确设置)。上面用this._super()方法调用了Person父类原来的init()和dance()方法说明了这一点。
我对这个结果仍是很满意的:它有助于加强‘classes’做为一个构造(structure)的概念,保持了简单的继承,并容许对父类的方法调用。
简单的类构造和继承
这有一个上面代码的实现(规模适度并且评论颇佳)-上下代码25行左右。反馈很好且被普遍接受。

// Inspired by base2 and Prototype
( function ( ) {
   var  initializing =  false , fnTest =  /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/ ;

 

  // The base Class implementation (does nothing)
  this.Class = function(){};
  
  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;
    
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
    
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" && 
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
            
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
            
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
            
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
    
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
    
    // Populate our constructed prototype object
    Class.prototype = prototype;
    
    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;学习

    // And make this class extendable
    Class.extend = arguments.callee;
    
    return Class;
  };
})();this

在我看来,最棘手的两个问题是 "initializing/Don't call init"和"create super method"。我想简单的涉及一下这些,以使你对这个方法中完成了什么有一个更好的理解。
初始化
为了用一个函数的prototype来模仿继承,咱们用传统的技术来建立一个父类函数的实例化并把它分配给子类的prototype。不考虑上面的内容的话其实现大抵像这样:
function  Person ( ) { }
function  Ninja ( ) { }
Ninja. prototype  =  new  Person ( ) ;
// Allows for instanceof to work:
( new  Ninja ( ) )   instanceof  Person  
这里面的挑战是,咱们须要利用instanceof的好处,而不是仅仅考虑实例化Person父类以及运行他的构造函数 的消耗。 为了中和这二者的效应,在咱们的代码中有一个变量 initializing,不管何时咱们想实例化一个类(惟一的目的是)来做为prototype的值,该变量都被设置为true。

所以当谈到实际的构造函数的时候,咱们要确信咱们不是在一个初始化模式,而是有条件的执行init方法:
if   (  !initializing  )
   this . init . apply ( this , arguments ) ;
尤为重要的是,init方法能够运行各类消耗很大的启动代码(链接到服务器,建立DOM元素,谁知道呢)因此绕过这个最终的工做会颇有好处。
super 方法
当你在作继承的时候,你建立一个类从父类中继承功能,一个常见的要求是你要能获取已经被你重写的方法。结果,在这个特别的实现中是一个新的临时方法._super。这个方法是惟一能够经过子类的方法来引用父类的相同方法的途径。
好比,若是你想经过这项技术来调用父类的构造函数,你能够这样作:
var  Person =  Class . extend ( {
  init:  function ( isDancing ) {
     this . dancing  = isDancing;
   }
} ) ;

 

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  }
});spa

var p = new Person(true);
p.dancing// => trueprototype

var n = new Ninja();
n.dancing// => false orm

实现这一功能是一个多步的过程。开始的时候,注意,咱们用来扩展一个已经存在的类的对象字面量 (好比被传入到Person.extend里的那一个)须要merge到基础的new Person实例(该实例的结构在前面已经被描述过)。在这个  merge 的过程当中,咱们作了一个简单的检查:咱们正在合并(merge)的属性是否是不一个函数?正在被咱们替换的是否是也是一个函数?若是条件成立的话咱们须要作一些事来建立一种方式使得咱们的父类方法依然能工做。
注意,咱们建立了一个匿名闭包(该闭包返回一个函数)来封装这个新的父类加强了的方法。开始的时候咱们须要作一个合格市民,把引用保存到老的this._super(若是它确实存在的话忽略它),在咱们作完相应工做后再恢复它。这对于有相同名字的变量已经存在的状况很是有用(不要期望能意外的换走它)。
接下来咱们建立新的_super方法,它仅仅是已经存在于父类prototype中的方法的一个引用。幸运的是咱们不须要作额外的变动,或者从新界定范围,当函数是咱们对象的属性的时候它的上下文环境将被自动设置(this将会指向咱们的实例而不是父类)。
最后咱们调用咱们原始的方法,在咱们恢复_super到它原始的状态而且从函数返回值以后它会完成它的工做(也可能要用到_super)。
针对上面的状况,已经有若干种有相似结果的方式能够实现(我已经看到了一种经过arguments.callee来绑定父类的方法到该方法自己的方式来实现),可是我感受个人这种技术提供了实用性和简洁性的最佳组合。
在我要完成的工做中我要覆盖更多的隐藏在JavaScript prototype背后的本质和细节,可是就这个Class类的实现,我想让更多的人来尝试它并运用它。我认为对于简洁的代码(更容易学习、扩展和下载)还有不少要说,因此我认为要了解JavaScript类构造和继承的基础,这个实现是一个很好的开始。(完)
相关文章
相关标签/搜索