分享

OpenLayers源码学习笔记---OpenLayers中的继承实现

 LibraryPKU 2014-06-05

 关于JS继承这块,建议大家先了解下JS中原型、原型链概念和prototype原子 属性,prototype可以供同类对象(使用new方法生成)的实例共享,这是js继承实现的最大依赖。(推荐《javascript高级程序设计》, 这本书中关于继承的实现和理论讲解很清楚)

     或许是一直以来从事面向对象开发,我总觉得这块的知识在框架中占有很重要的地位。JavaScript语言因为其特性,即使做面向对象的开发,无法实现 静态多态这个特性,封装性也很菜(基本谈不上),剩下的就是动态多态和复用性,个人觉得最重要的就是复用了。复用性只要针对的是的是方法,一方面是代码的 复用,另一方面在JS中算是方法作为对象占有比较大的内存开销,至于属性一般用来记录数据信息,其复用暂且不提。

OpenLayers中的继承相关都写在BaseTypes/OpenLayers.Class.js中。

简单的类定义,继承使用的方式如下:    

MySubClass = Openlayers.Class({
                          //父类属性方法
                   })
 MyClass  = OpenLayers.Class(MySubClass,{
                          //这里写我们的子类属性方法。
                   })

这里直接贴出源码分析

OpenLayers.Class = function() {
    var len = arguments.length;
    var P = arguments[0];
    var F = arguments[len-1];
    var C = typeof F.initialize == "function" ?  F.initialize : function(){ P.prototype.initialize.apply(this, arguments); };
    if (len > 1) {
        var newArgs = [C, P].concat( Array.prototype.slice.call(arguments, 1, len));
        OpenLayers.inherit.apply(null, newArgs);
    } else {
        C.prototype = F;
    }
    return C;
};
 
OpenLayers.inherit = function(C, P) {
   var F = function() {};
   F.prototype = P.prototype;
   C.prototype = new F;
   var i, l, o;
   for(i=2, l=arguments.length; i<l; i++) {
       o = arguments[i];
       if(typeof o === "function") {
           o = o.prototype;
       }
       OpenLayers.Util.extend(C.prototype, o);
   }
};
OpenLayers.Util = OpenLayers.Util || {};
OpenLayers.Util.extend = function(destination, source) {
    destination = destination || {};
    if (source) {
    //属性复制
        for (var property in source) {
            var value = source[property];
            if (value !== undefined) {
                destination[property] = value;
            }
        }
        var sourceIsEvt = typeof window.Event == "function"
                          && source instanceof window.Event;
        if (!sourceIsEvt
           && source.hasOwnProperty && source.hasOwnProperty("toString")) {
            destination.toString = source.toString;
        }
    }
    return destination;
};

分析下代码会发现:OpenLayers.util.extend()方法是个属性复制的过程,比较简单。继承的主要实现来在OpenLayers.Class和OpenLayers.inherit;

假设使用MySubClass调用来调试走一遍代码:

父类没有继承,所以调用OpenLayers.Class只有一个参数(按照规范我们传入的对象有一个initialize()构造函数,而使用JS函数对象来模拟继承对象一直是比较流行的方式。)

var C = typeof F.initialize == "function" ?
                 F.initialize :
                 function(){ P.prototype.initialize.apply(this, arguments); };
 C.prototype = F;
 return C;

这里获取initialize方法作为最终的对象引用,其prototype属性指向传入的Object,这样我们写在 Object中都所有属性和方法都可以被F(initialize)通过原型链获取并且使用。利弊:所有的方法都达到了公用的目的,但是属性本身位于 prototype中,则在设置属性的时候等于在对象本身C上添加了格外属性,考虑多个对象属性基本很难公用的问题,这里基本没错,唯一可惜的就是在 prototype中多用了些属性内存开销。

结论:     C = Object.initilize();

            C.prototype = Object;

 

使用MyClass来分析下继承。

首先还是获取对象构造方法,但是如果没有写则返回父类的构造方法,注意父类的方法不是直接返回,而是调用,相当于获取了父类构造方法另一个副本

var C = typeof F.initialize == "function" ?
        F.initialize :
        function(){ P.prototype.initialize.apply(this, arguments); };

接下来调用继承。(注:这里继承多个父类很少用到(个人觉得这里不支持继承多个父类,这里的写法是为了分开定义子类中的方法和属性(也可视为未子类添加额外属性方法),有兴趣可以自行研究),所以只分析继承一个父类的情况)

var newArgs = [C, P].concat(
                Array.prototype.slice.call(arguments).slice(1, len-1), F);
OpenLayers.inherit.apply(null, newArgs);

所以最终调用相当于是   OpenLayers.inherit(C , P , F);

              var F = function() {};

              F.prototype = P.prototype;

              C.prototype = new F;

     结果:

           C = initialize();

           C.prototype = function();(F,只是一个引用,无多大意义)

           C.prototype._proto_ == P.prototype

   考虑P就是我们前面所得到的父类MySubClass,那么C即可以通过原型链 访问到P所有的属性和方法,继承就差不多实现了。

for(i=2, l=arguments.length; i<l; i++) {
               o = arguments[i];
               if(typeof o === "function") {
                   o = o.prototype;
               }
               OpenLayers.Util.extend(C.prototype, o);
           }

      这段代码如果只考虑单父类继承的情况,则o = F,即我们的子类Object对象,而o本身不是方法,最终调用如下:

     OpenLayers.Util.extend(C.prototype, Object);

     即把我们所有的属性,方法都赋值到C.prototype上,一方面保证了同对象上属性的公用,另一方面考虑在原型链层,Object定义的属性和方法 相比父类的方法更高一层,C.prototype 相比C.prototype._proto_,所以调用过程中优先使用Object中的定义,相当于继承中的函数覆写。

     关于调用父类同名(被覆写了)方法,因为不存在super指针,所以这里在使用的时候需要全名调用MySubClass.XXX.apply(this,args);

     至于属性复制使用将构造函数也顺道赋值了一遍,C.prototype.initilize===C 本身,这个链接的存在有什么特殊效果没看出来

      C.prototype.CLASS_NAME  按照规范存在着类型名称信息,可以提供使用中的类别判断

 

补充:

       使用new XXX()得到的对象内部有个constructor指针指向原来的XXX方法(对象)本身,但是这里我们使用initialize方法来模拟构造函数, 所以得到的方法最终构造函数指向的也是XXX.initialize方法,则无法使用构造函数来直接判断一个对象具体类型。

        关于instanceof方法,判断一个对象是否为某一类数据类型,或者一个变量是否是一个对象的实例,这个方法的判断是依赖原型链的,A instanceof B,即判断A的原型链中是否存在B,亦即判断A的prototype属性(因为A是对象实例,所以这个属性在大多浏览器中不可见,在chrome等一些浏 览器中用_proto_名称表示)是否包含B的prototype,这里B一般是个对象定义或者数据类型。

       依照这个判断,我们实现的继承结果中,如果A是MySubClass的示例,则A.prototype和MySubClass的prototype相 同,使用instanceof的时候返回true,又因为MySubClass继承自MyClass,以上面的继承关系推 断,A.prototype.prototype == myClass.Prototype,及调用A  instanceof MySubClass也会返回true。结论:我们可以使用intanceof 来判断一个变量是否是某个对象的实例。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多