思路主要来源于 YUI3 component infrastructure ,利用 mixin 机制模拟 c++ 的多继承 机制,多继承争论很多,但是谨慎使用确实会带来设计的简洁明了,避免采用 Extjs 式的单继承所带来的深层结构以及由于功能聚集导致的巨型类,且功能不能在多个组件中共享。
多继承:
主要依据 c++ 规范,当多继承时,注意构造函数和析构函数的合适触发顺序,举例:
弹窗
用 c++ 多继承来设计弹窗组件,分析弹窗的功能,将弹窗理解为
1.是一个弹出层
2.具备头,体,尾组成部分
3.具备拖拽功能
4.可以被关闭
伪代码:
class StdMod { private: header; body; footer; } class Close { private: closable; } class Drag { private: draggable; handlers; } //待定义 class Overlay{ //... } class Dialog : Overlay,StdMod,Close,Drag { }
弹出层
而弹出层又是一个复杂的东西,是很多功能的聚集体
1.是一个方形可渲染容器
2.可以被定位,隐藏
3.可以具备背景遮罩功能
伪代码:
class Box { private: width; height; } class Position { private: x; y; visible; } class Mask { private: maskable; } class Overlay : Box,Position,Mask{ }
弹层的生成和销毁
生成:
首先初始化基类的成员并执行基类的构造器,多个基类的构造器执行顺序,根据继承时的申明顺序指定,然后执行底层类的构造器体。
即:绑定用户值给对应的属性并执行 Box ,Position ,Mask 的构造器,然后执行 Overlay 的构造器。
销毁:
和构造器的执行顺序完全相反,只不过变成了析构器。
javascript 模拟:
而对于 javascript (有人说和c++同级... ),当然没有这些完善的对象生命周期管理,一切都得靠自己,除此之外还要加上不少的开发约定。我的实现是:这些所有构造与析构的依赖顺序全部扔给最顶层的类 Base 处理,并且由于原型链的存在使得 js 天生只能实现单继承( SI ),那么在多继承的情况下,除了第一个类,其他类只能通过 mixin 的方式混给最终类。
类的产生:
通过 Base.create(cls,[clsExt1, clsExt2,... ],prototype,staticProperty) 产生一个新构造器,该构造器的继承链上一个构造器是 cls (cls必须继承或本身就是 Base),clsExt 为扩展类数组,通过 mixin 将自己的代码混合在最终类,并将扩展类集合的构造器挂靠在新生成的类上面。
结构:
类的实例化:
当实例化时,统一委托 Base 的构造器就行,主要进行:
1.从顶层类开始,执行挂靠在顶层类上的扩展类构造器方法。
2.然后执行顶层类的 init 原型方法
3.依次沿继承链向下执行 1,2 步
类实例的销毁化:
当调用实例的 destroy 方法时,委托 Base 的原型 destroy 方法进行沿继承链的销毁工作,主要进行:
1.执行当前构造器原型的destructor方法
2.调用挂靠在当前类的扩展类构造器原型的__destructor方法,注意顺序和实例化时相反
3.依次沿继承链向上执行1,2 步。
最终代码:
则Overlay的最终代码为:
var Overlay = Base.create(Base,[S.Ext.Box, S.Ext.ContentBox, S.Ext.Position, S.Ext.Shim, S.Ext.Mask], { init:function() { S.log("Overlay init"); }, destructor:function(){ S.log("Overlay destructor"); } });
初始化顺序:
//用户扩展多继承的 UIBase 组件 function Position() {} Position.ATTRS = { x:{}, y:{}, xy:{} } Position.prototype = { __renderUI: fn, __bindUI: fn, __syncUI: fn, _uiSetX: fn, __destructor: fn }