Ruby's Louvre

每天学习一点点算法

导航

javascript框架之继承机制3

继续上一部分,现在我们的实的构造器initialize很完美了,甚至连类式super这样语法糖都不用了,就自动实例了父类。我们转而看一看其属性与方法的继承。许多类库都是一个for...in循环再加一些判定实现原型属性拷贝,或根据这些判定把某些属性揪出来加工一下再放进去。又如,我们要对Array的模板进行扩展,做成一个新类Array2,直接继承后,在有些浏览器中Array2可能有forEach方法,可能没有,如果没有,我们才添加自己实现的forEach方法。因此条件过滤非常重要的。在mootools中,生成类都带有一些类方法(alias与implement),以供更进一步的加工。

   var copy = function(a,b,c){
    var l = arguments.length;
    if(a && b && l == 2){
      for(var p in b)
        a[p] = b[p];
    }else if(a && b && l == 3 ){
      a[b] = c;
    }
    return a;
  };

上面的方法名副其实,就是用于单纯的复制。

   var override = function(a,b,filter,scope){
        var i, scope = scope || window;
        for(i in b) {
          if(filter.call(i,a,b))
             a[i] = b[i];
        }
     };

override 为有选择地复制,如果我们不想覆盖原生函数,只需要这样:

   override(Array,{/**/},function(p,a,b){
            return !(p in a)
    })

有了以上方法,我们就可以设置更为复杂的语法糖,如实例属性的getter与setter,它们在java早已用annotation搞定了,在ruby中它们的设置也非常简单,我们没有道理放弃如此诱人的东西。不过,在javascript中我们无法利用注释来实现,因为可恨的火狐在编译时把注释全部去掉。ruby那种实现,相当于让人设计另一套语法。因此,我们还是交由类工厂实现,把它们全部变成原型方法。过程如下,首先取得我们定义的构造方法,然后取得其参数,再取得父类构造器的参数,然后转化为两个数组,如果子类参数数组的长度大于父类的,说明它定义了新的属性,我们把这些新属性提出来,然后把它们首字母大写前面加上set与get,对应的函数用eval生成即可。

          //获取函数的参数,以字符串数组形式返回
          var argumentNames = function(fn) {
            var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
            .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
            .replace(/\s+/g, '').split(',');
            return names.length == 1 && !names[0] ? [] : names;
         }
          //首字母大写函数
         var capitalize = function(str){
            return str.replace(/\S+/g, function(a){
              return a.charAt(0).toUpperCase() + a.slice(1);
            });
          };
          //用于提取两个数组不同的部分
         var difference = function(arrayA,arrayB){
           var len = Math.max(arrayA.length, arrayB.length),
            i,p, result=[],  diff = {};
          for(i=0;i<len;i++){
            if(i<arrayA.length)
              diff[ arrayA[i] ] = diff[arrayA[i]] ? 2 : 1;
            if(i<arrayB.length)
              diff[ arrayB[i] ] = diff[arrayB[i]] ? 2 : 1;
          };
          for(p in diff)
            if(diff[p] ==1 ) result.push(p);
          return result;
          };

在火狐等浏览器中,公开了一个叫__proto__的内部变量,它为实例对象如我们的i的属性。通常实例都有一个construtor属性,它其实也是prototype上。但prototype为类的属性,实例只能访问prototype上的属性。

var i = new IndiaTiger("印度虎",2,"印度");
var a = new Array
alert(i.prototype)//undefined,实例不能直接访问类的prototype属性
alert(a.prototype)//undefined
alert(i.constructor)//IE不能访问,其他能           --> function IndiaTiger(){[variant code]}
alert(a.constructor)//IE不能访问,其他能            --> function Array(){[native code]}          
alert(i.__proto__.klassname)//IE不能访问,其他能       IndiaTiger  
alert(i.__proto__.constructor)//IE不能访问,其他能  --> function IndiaTiger(){[variant code]}
alert(a.__proto__.constructor)//IE不能访问,其他能  --> function Array(){[native code]}      

这简单,我们为我们生产的类的prototype上添加一个__proto__属性即可。这样另一条原型链在我们的继承体系中就修复了!

   if(!+"\v1" || window.opera){
         klass.prototype.__proto__ = klass.prototype;//让类实例可以访问类的prototype
   }

我们来看另一种语法糖,它见于Prototype.js1.6版。

//以下为Prototype的类继承
var Person = Class.create({
  initialize: function(name) {
    this.name = name;
  },
  say: function(message) {
    return this.name + ': ' + message;
  }
});

// when subclassing, specify the class you want to inherit from
var Pirate = Class.create(Person, {
  // redefine the speak method
  say: function($super, message) {//★★★★注意看是如何重写父类的方法
    return $super(message) + ', yarr!';
  }
});

var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"

其实就是Ruby那一套东西,目的是达到最大的复用,类继承是整体的复用,方法的super是局部的复用(我也不知怎样叫它)。子类的同名方法把父类的方法用裹其中,大大减少方法的代码长度。我们看一下相应的ruby代码:

class A         #定义父类
  def a         #定义方法 相当于javascript的 function a(){document.write("a 1")};
    p 'A a method'
  end
end

aa = A.new  #创建实例 var aa = new A;
aa.a           # aa.a();输出 "A a method"
#>ruby a.rb
#"A a method"
#>Exit code: 0

像ruby这种一切皆对象的语言中,实现继续轻而易举!

class A      #定义父类
  def a 
    p 'A a method'
  end
end

class B < A#定义子类,让类B继承类A
  def a
    p 'B a method start'
    super
    p 'B a method end'
  end
end

b = B.new
b.a
#>ruby a.rb
#"B a method start"
#"A a method"
#"B a method end"
#>Exit code: 0

因此我们应该明白Prototype的$super就是执行与父类的同名方法。如何调用它呢?我们可以为$super方法传入arguments,通过arguments我们在内部就可以找到此方法实体(那个arguments.callee),为了方便找到父类的同类方法,我们在它直接设置到方法上。

http://www.davidflanagan.com/2008/07/method-chaining-in-javascript-inheritance-hierarchies.html

DOMは人間の使う物ではない!

posted on 2009-11-09 17:17  司徒正美  阅读(2076)  评论(3编辑  收藏  举报