jqueryUI之widget
jqueryUI widget 分析
$.widget 理解是个注册ui组件的工厂方法,$.widget(name,prototype) 传入ui的name和ui的prototype,就会返回新ui的构造方法。
$.widget( "ui.buttonset",/* 原型*/ { version: "1.10.1", options: { items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)" }, _create: function() { this.element.addClass( "ui-buttonset" ); }, _init: function() { this.refresh(); },
因为ui组件有很多方法是可以公用的,像共有属性、option参数设置、create/init组件执行过程、on/off/trigger事件和其他focusable等添加class的逻辑,通过$.Widget这个ui的最基类做了封装
$.Widget = function( /* options, element */ ) {}; $.Widget._childConstructors = []; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", defaultElement: "<div>", options: { disabled: false, create: null }
如果简化ui的$.widget的逻辑
function base(){};//$.Widget 基类
base.prototype={ _create:function(){... }, ... };
function pkg(proto){//$.widget 工厂方法
var constructor=function(opt){ this._create(opt); };
constructor.prototype=$.extend(true,{},new base(),proto);
return constructor;
}
源码分析
实际的实现中$.widget做了一些自己的特殊处理:
如命名相关
var fullName, existingConstructor, constructor, basePrototype, // proxiedPrototype allows the provided prototype to remain unmodified // so that it can be used as a mixin for multiple widgets (#8876) proxiedPrototype = {}, namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; fullName = namespace + "-" + name; if ( !prototype ) { prototype = base; base = $.Widget; } // create selector for plugin $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { return !!$.data( elem, fullName ); }; $[ namespace ] = $[ namespace ] || {}; existingConstructor = $[ namespace ][ name ];
如快捷使用
constructor = $[ namespace ][ name ] = function( options, element ) { // allow instantiation without "new" keyword if ( !this._createWidget ) { return new constructor( options, element ); } // allow instantiation without initializing for simple inheritance // must use "new" keyword (the code above always passes args) if ( arguments.length ) { this._createWidget( options, element ); } };
如考虑继承情况
// extend with the existing constructor to carry over any static properties $.extend( constructor, existingConstructor, { version: prototype.version, // copy the object used to create the prototype in case we need to // redefine the widget later _proto: $.extend( {}, prototype ), // track widgets that inherit from this widget in case this widget is // redefined after a widget inherits from it _childConstructors: [] }); // If this widget is being redefined then we need to find all widgets that // are inheriting from it and redefine all of them so that they inherit from // the new version of this widget. We're essentially trying to replace one // level in the prototype chain. if ( existingConstructor ) { $.each( existingConstructor._childConstructors, function( i, child ) { var childPrototype = child.prototype; // redefine the child widget using the same prototype that was // originally used, but inherit from the new version of the base $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); }); // remove the list of existing child constructors from the old constructor // so the old child constructors can be garbage collected delete existingConstructor._childConstructors; } else { base._childConstructors.push( constructor ); }
和桥接ui组件成jquery插件
$.widget.bridge( name, constructor );
还有很多属性复制克隆防止覆盖的处理
basePrototype.options = $.widget.extend( {}, basePrototype.options );
比较难以理解的应该是这段
$.each( prototype, function( prop, value ) { if ( !$.isFunction( value ) ) { proxiedPrototype[ prop ] = value; return; } proxiedPrototype[ prop ] = (function() { var _super = function() { return base.prototype[ prop ].apply( this, arguments ); }, _superApply = function( args ) { return base.prototype[ prop ].apply( this, args ); }; return function() { var __super = this._super, __superApply = this._superApply, returnValue; this._super = _super; this._superApply = _superApply; returnValue = value.apply( this, arguments ); this._super = __super; this._superApply = __superApply; return returnValue; }; })(); });
这段代码在传入的ui原型中有方法调用this._super 和this.__superApply会调用到base上(最基类上)的方法。参考测试用例中
test( "._superApply()", function() { expect( 10 ); var instance; $.widget( "ui.testWidget", { method: function( a, b ) { deepEqual( this, instance, "this is correct in testWidget" ); deepEqual( a, 5, "parameter passed to testWidget" ); deepEqual( b, 10, "second parameter passed to testWidget" ); return a + b; } }); $.widget( "ui.testWidget2", $.ui.testWidget, { method: function( a, b ) { deepEqual( this, instance, "this is correct in testWidget2" ); deepEqual( a, 5, "parameter passed to testWidget2" ); deepEqual( b, 10, "second parameter passed to testWidget2" ); return this._superApply( arguments ); } }); $.widget( "ui.testWidget3", $.ui.testWidget2, { method: function( a, b ) { deepEqual( this, instance, "this is correct in testWidget3" ); deepEqual( a, 5, "parameter passed to testWidget3" ); deepEqual( b, 10, "second parameter passed to testWidget3" ); var ret = this._superApply( arguments ); deepEqual( ret, 15, "super returned value" ); } }); instance = $( "<div>" ).testWidget3().testWidget3( "instance" ); instance.method( 5, 10 ); delete $.ui.testWidget3; delete $.ui.testWidget2; });
jqueryui 的widget设计,即能很好的保证代码复用和维护风格统一,也允许各自ui灵活的实现功能。除此之外jquery ui在api的灵活性和实例化步骤等地方都做的很好,在自己实现代码的时候参考下他的实现很有启发