• 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は人間の使う物ではない!
  • 相关阅读:
    Matlab smooth函数原理
    Pandas中的高级索引loc、iloc、ix精简概括
    QT常见错误:"multiple definition of xxx"
    Github术语解释
    数据反转 LSB
    LSB最低有效位和MSB最高有效位
    Modbus通信CRC16校验程序
    CRC16常见几个标准的算法及C语言实现
    DB9 公头母头引脚定义及连接
    hdu 2577 How to Type(dp)
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1598218.html
Copyright © 2020-2023  润新知