YUI3中为了避免不必要的类继承层次,以及摆脱利用原型链模拟的单继承模型,除了利用Y.augment 来扩充单一类功能的机制,对用多功能扩充更提出了扩展类的概念(另一个和augment的不同点在于扩充功能类的构造函数调用时机),能够方便的将功能分解,对应 Base.create。
利用官方例子解释:
比如某个类继承了Widget但是它想有Stack,Position的功能,那么按照传统设计就要该做继承WidgeStackPosition,而WidgeStackPosition再继承Widget,这两个功能实际上绑定在一起了,而对于使用者这种深层的类链是不必要的,使用者只需知道这是个Widget,它有stack,position的功能即可。于是yui3有了Base.create功能:
var Positionable = Y.Base.create("positionable", Y.Widget, [Y.WidgetStack,Y.WidgetPosition]);
动态生成了一个类,这个类是Widget的子类,但并不是WidgetStack或WidgePositon的子类,并且这两个功能互相独立,WidgetStack,Position也很简单只是一个单纯类,描述了功能,其并没有继承Widget,这样就完成了是Widget的子类但具有Stack,Positon功能这一需求,避免了继承的滥用以及功能的灵活配置。
原理:
简单的说:利用了脚本语言的动态特性,将extension class的prototype混合在新生成类的prototype中,使新生成类具有了扩展类的特有功能。
但是这样的话,就丧失了传统类继承时yui3 oop的一些特性,比如Stack可以利用静态 ATTRS 来设置自己的属性,更关键的是只赋值功能代码的话,Stack的构造初始化函数没有机会运行,而构造函数中也许存在他的功能函数所依赖的状态。
具体的说:create干了很多事,并且需要Base的配合,这也就是为什么create要求的基类一定要继承自Base:
1。不仅要复制extension class的prototype功能函数到新生成类,还要复制extension class的静态属性到新生成类。
2。在初始化新生成类的实例时,要调用extension class的构造函数,这点正是 Base 基类初始化所考虑的,所以新生成类一定要以Base为基类,那么在初始化新生成类实例时,调用Base的初始构造函数,这时再调用新生成类所有 extension class 的构造函数,于是执行了扩展类的初始化:
//Base._initHierarchy : //...... for (ci = classes.length - 1; ci >= 0; ci--) { constr = classes[ci]; constrProto = constr.prototype; //初始化继承链上对应类的扩展类 if (constr._yuibuild && constr._yuibuild.exts) { for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) { constr._yuibuild.exts[ei].apply(this, arguments); } } //..... }
换个实现看:
根据以上的分析,对应于:
var Positionable = Y.Base.create("positionable", Y.Widget, [Y.WidgetStack,Y.WidgetPosition]);
也即简化为这样的实现,加深理解:
function Positionable() { Positionable.superclass.constructor.apply(this, arguments); } Positionable._yuibuild = Positionable._yuibuild || {}; Positionable._yuibuild.exts = Positionable._yuibuild.exts || []; //Base调用初始化 Positionable._yuibuild.exts.push(Y.WidgetStack); Positionable._yuibuild.exts.push(Y.WidgetPosition); Positionable.NAME = "positionable"; Y.extend(Positionable, Y.Widget); //属性 Y.mix(Positionable, Y.WidgetStack); //功能代码prototype Y.mix(Positionable.prototype, Y.WidgetStack.prototype); Y.mix(Positionable, Y.WidgetPosition); Y.mix(Positionable.prototype, Y.WidgetPosition.prototype)