现在的位置: 首页 > 综合 > 正文

javascript框架之继承机制2

2014年02月04日 ⁄ 综合 ⁄ 共 12024字 ⁄ 字号 评论关闭

 我们来模仿一下最OO的mootools的继承机制。它的类都有一个叫做initialize构造方法,这与Java的类都有一个与类名同名的构造方法一样的道理。只不过,这些叫initialize或init都是借鉴自Prototype,而Prototype那帮人是Ruby出身。为了区别mootools那种污染原生方法的做法,我把类的构造器命名为variant,并且禁止查看构造方法(像浏览器禁止查看原生对象的构造方法那样)。

 
    var variant = function (options){
      options = options || {};
      var initialize = options.initialize || function(){};
      var klass = initialize ;
      klass.constructor = arguments.callee;
      klass.prototype.constructor = klass;
      klass.toString = function(){//禁止查看构造方法
        return "function variant(){/n    [variant code]/n}"
      }
      return klass;
    };

    var Person = variant({initialize:function(age){
        this.age = age;
      }});
    alert(Person)//看不到构造方法的实现
    var p = new Person(3);
    alert(p)
    alert(p.age);
    alert(p.constructor);//看不到构造方法的实现
    var P = variant({})
    var a = new P;
    alert(a);
 
运行代码

    var variant = function (options){
      options = options || {};
      var initialize = options.initialize || function(){};
      var klass = initialize ;
      klass.constructor = arguments.callee;
      klass.prototype.constructor = klass;
      klass.toString = function(){//禁止查看构造方法
        return "function variant(){/n    [variant code]/n}"
      }
      return klass;
    };
这是一个非常简单的工厂方法,用于生产类的。options就是一属性包,可能装有我们的类的构造方法,我们要做的是把它提取出来,然后输送出去。

     function factory(a){
         a.b = b;
         a.c = c;
         return a;
      }
不过,这就有点像倒爷,我们应该像水果商,从果农收购水果回来包装一番,才卖出去。这包装就是往要生成的类添加各种原型方法与类方法。我们进一步打造我们的类工厂,让生产的类拥有继承能力,也就是把第一部分的makeBridge 加上去。

 
    var variant = function (options){
      options = options || {};
      var initialize = options.initialize || function(){};
      var superclass = options.inherit;
      delete options.initialize;
      delete options.inherit;
      var klass = initialize ;
      if(superclass){//如果是通过继承得来的属性
        var bridge = function() {};
        bridge.prototype = superclass.prototype;
        klass.prototype = new bridge;
        klass.prototype.superclass = superclass;
      }
      for(var i in options){//mixin
        klass.prototype[i] = options[i]
      }
      klass.constructor = arguments.callee;//类的静态属性
      klass.prototype.constructor = klass;//真正用来创建实例的

      klass.toString = function(){
        return "function variant(){/n    [variant code]/n}"
      }
      return klass;
    };
    var Animal = variant({
      initialize:function(name){
        this.name = name;
      },
      getName:function(){
        return "这是" +this.name;
      }
    });
    var a = new Animal("动物");
    alert(a.name)
    alert(a.getName())
    var Tiger = variant({
      inherit:Animal,
      initialize:function(name,age){
        this.name = name;
        this.age =age;
      },
      getAge : function(){
        return this.age;
      },
      setAge : function(age){
        this.age = age;
      }
    })
    var t = new Tiger("老虎",10);
    alert(t.age)
    alert(t.getName())
    t.setAge(11);
    alert(t.getAge());
  //  alert(a.getAge());动物实例并没有此方法
 
运行代码

prototype继承是通过把子类的原型设置成父类的一个实例来进行继承的。因此无论是inherit也好,mixin也好,都是往子类的原型添加东西。打个比方,继承就是把一大堆属性与方法直接加在子类的原型上,mixin则相当于把一打属性与方法逐一加到构造函数的原型。不过在上面Tiger类的构造器写得有点不好,因为name属性本来父类就有,子类就不用定义一次。如果父类有许多实例属性,岂不是要写一大打赋值语句。应该改为

    var Tiger = variant({
      inherit:Animal,
      initialize:function(name,age){
        this.superclass(arguments);//this.name = name
        this.age =age;
      },
      getAge : function(){
        return this.age;
      },
      setAge : function(age){
        this.age = age;
      }
    })
但是这种做法在第三代子类就行不通了,比如我们弄个子类叫IndiaTiger,它比Tiger多出一个类例属性location。

var IndiaTiger = variant({
        inherit:Tiger,
        initialize:function(name,age,location){
          this.superclass(arguments);
          this.location =location;
        }
      });
 
   var variant = function (options){
        options = options || {};
        var initialize = options.initialize || function(){};
        var superclass = options.inherit;
        delete options.initialize;
        delete options.inherit;
        var klass = initialize ;
        if(superclass){//如果是通过继承得来的属性
          var bridge = function() {};
          bridge.prototype = superclass.prototype;
          klass.prototype = new bridge;
          klass.prototype.superclass = superclass;
        }
        for(var i in options){//mixin
          klass.prototype[i] = options[i]
        }
        klass.constructor = arguments.callee;//类的静态属性
        klass.prototype.constructor = klass;//真正用来创建实例的

        klass.toString = function(){
          return "function variant(){/n    [variant code]/n}"
        }
        return klass;
      };
      var Animal = variant({
        initialize:function(name){
          this.name = name;
        },
        getName:function(){
          return "这是" +this.name;
        }
      });
      var Tiger = variant({
        inherit:Animal,
        initialize:function(name,age){
          this.superclass(arguments)
          this.age =age;
        },
        getAge : function(){
          return this.age;
        },
        setAge : function(age){
          this.age = age;
        }
      });
      var IndiaTiger = variant({
        inherit:Tiger,
        initialize:function(name,age,location){
          this.superclass(arguments);
          this.location =location;
        }
      });
      try{
        var i = new IndiaTiger("印度虎",2,"印度");
        alert(i.getName());
        i.setAge(3);
        alert(i.getAge());
        alert(i.location);
      }catch(e){
        alert("报错了");
        alert(e);
      }
 
运行代码

当new印度虎实例时就报错了,除非其父类的构造器没有用到this.superclass(arguments)。不用说,问题是出自this,它的不确定性总是为我们惹很多麻烦。这里的this总为IndiaTiger 的实例,因此this.superclass总为Tiger ,也因此我们无法实例化Animal 。javascript的实例化过程是,有父类先实例化父类,然后再到下级子类。由于我们无法通过klass.prototype.superclass获取Animal,我们可以用klass.superclass来试一下。klass为类,而this为实例:

IndiaTiger.superclass.apply(this, arguments);
//this为IndiaTiger 的实例
//arguments为IndiaTiger 构造器的参数对象
但我们不能把前面的IndiaTiger 写死,因为创建IndiaTiger 这个类时,它还不知自己叫IndiaTiger ,我们可以通过arguments.callee获取IndiaTiger自身。Tiger的构造相仿。

        var IndiaTiger = variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            arguments.callee.superclass.apply(this, arguments);
            this.location =location;
          }
        });
我们可以把它再抽取出来,这样每次就不用写这么长的代码了。

        function _super(o, args) {//o为子类的实例,agrs为子类构造的arguments对象
            return args.callee.superclass.apply(o, args);
        }
上面的代码已经假设了,它的构造器总会有inherit这个属性,但如果没有岂不是会报错,另,它还假设了我们的类上面有一个属性叫superclass,因此我们要在类工厂中做相应的调整。添加或修改如下两行代码:

  var superclass = options.inherit || Object;
  klass.superclass = superclass; //类的superclass
 
        function _super(o, args) {//o为子类的实例,agrs为子类构造的arguments对象
            return args.callee.superclass.apply(o, args);
        }
        var variant = function (options){
          options = options || {};
          var initialize = options.initialize || function(){};
          var superclass = options.inherit || Object;
          delete options.initialize;
          delete options.inherit;
          var klass = initialize ;
          var bridge = function() {};
          bridge.prototype = superclass.prototype;
          klass.prototype = new bridge;
          klass.prototype.superclass = superclass;//实例的superclass
          for(var i in options){//mixin
            klass.prototype[i] = options[i]
          }
          klass.constructor = arguments.callee;//类的静态属性
          klass.prototype.constructor = klass;//真正用来创建实例的
          klass.superclass = superclass; //类的superclass
          //  klass.toString = function(){
          //    return "function variant(){/n    [variant code]/n}"
          //  }
          return klass;
        };
        var Animal = variant({
          initialize:function(name){
            this.name = name;
          },
          getName:function(){
            return "这是" +this.name;
          }
        });
        var Tiger = variant({
          inherit:Animal,
          initialize:function(name,age){
            _super(this,arguments)
            this.age =age;
          },
          getAge : function(){
            return this.age;
          },
          setAge : function(age){
            this.age = age;
          }
        });
        var IndiaTiger = variant({
          inherit:Tiger,
          initialize:function(name,age,location){
             _super(this,arguments);
            this.location =location;
          }
        });
        var i = new IndiaTiger("印度虎",2,"印度");
        alert(i.getName());
        i.setAge(3);
        alert(i.getAge());
        alert(i.location);
        alert(i.superclass)//我们暂时撤去toString方法,它将会弹出其父类的构造器initialize
    
 
运行代码

不过每次设置新类的构造器时都要添加一行_super(this,arguments)也太麻烦了,最好把它隐藏起来,内部调用。另,把_super方法放到工厂外,显得太松散,既然也是用来构建类,因此也该把它整合到类工厂中。

      function factory(a){
         var b = function(){
             s();
             a();
             //*****其他方法
         }
         return b
      }
也就是说,我们只要这样设置类的构造器即可:

        var IndiaTiger = Variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            this.location =location;//★★★★★
          }
        });
 
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <title>类</title>

    <script type="text/javascript" charset="utf-8">
      window.onload = function(){

        var Variant = function (options){
          options = options || {};
          var initialize = options.initialize || function(){};
          var superclass = options.inherit || Object;
          var klassname = options.klassname ;
          delete options.initialize;
          delete options.inherit;
          var klass = function() {
            superclass.apply(this, arguments); 
            initialize.apply(this, arguments)
          };
          var bridge = function() {};//继承父类
          bridge.prototype = superclass.prototype;
          klass.prototype = new bridge;
          klass.prototype.superclass = superclass;//实例的superclass
          klass.superclass = superclass; //类的superclass,用于创建父类的实例

          for(var i in options){//mixin
            klass.prototype[i] = options[i]
          }

          klass.constructor = arguments.callee;//类的静态属性
          klass.prototype.constructor = klass;//用于创建当前类的实例

          var getKlassContext = function(c) {
            var e = c.toString().replace(/[/s/?]/g,"");
            getKlassContext = function() {
              return e;
            };
            return getKlassContext();
          };
          var getKlassName = function(search,context){
            var search = search.toString().replace(/[/s/?]/g,""),
            last = search.length >= 50 ? 50 :search.length;
            search = search.substring(0,last);
            var end = context.indexOf(search),start = end-100;
            start = start < 0 ? 0 :start;
            var str = context.substring(start,end);
            str = str.match(/var(/w+)/=Variant/);
            return (str && str[1]) ? str[1] :"Object";
          };
          if(!klassname){
            context = getKlassContext(arguments.callee.caller);
            klassname = getKlassName(initialize,context);
            if(klassname == "Object"){
              throw Error("如果没有klassname就必须显式设置initialize");
            }
          }
          klass.klassname = klassname;
          klass.prototype.klassname = klassname;
          klass.toString = function(){
            return ("function "+klassname+"(){/n    [variant code]/n}");
          }

          return klass;
        };

        var Animal = Variant({
          initialize:function(name){
            this.name = name;
          },
          getName:function(){
            return "这是" +this.name;
          }
        });
        var Tiger = Variant({
          inherit:Animal,
          initialize:function(name,age){
            this.age =age;
          },
          getAge : function(){
            return this.age;
          },
          setAge : function(age){
            this.age = age;
          }
        });
        var IndiaTiger = Variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            this.location =location;
          }
        });

        var i = new IndiaTiger("印度虎",2,"印度");
        alert(i.klassname);
        alert(i.getName());
        i.setAge(3);
        alert(i.getAge());
        alert(IndiaTiger);
        alert(Tiger);
      }

    </script>
  </head>
  <body>
  <pre>
        var Animal = Variant({
          initialize:function(name){
            this.name = name;
          },
          getName:function(){
            return "这是" +this.name;
          }
        });
        var Tiger = Variant({
          inherit:Animal,
          initialize:function(name,age){
            this.age =age;
          },
          getAge : function(){
            return this.age;
          },
          setAge : function(age){
            this.age = age;
          }
        });
        var IndiaTiger = Variant({
          inherit:Tiger,
          initialize:function(name,age,location){
            this.location =location;
          }
        });

        var i = new IndiaTiger("印度虎",2,"印度");
        alert(i.klassname);
        alert(i.getName());
        i.setAge(3);
        alert(i.getAge());
        alert(IndiaTiger);
        alert(Tiger);
  </pre>
  </body>
</html>
 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cheng5128/archive/2009/11/07/4787834.aspx

抱歉!评论已关闭.