OpenLayers源码学习笔记---OpenLayers中的继承实现
关于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 来判断一个变量是否是某个对象的实例。