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的灵活性和实例化步骤等地方都做的很好,在自己实现代码的时候参考下他的实现很有启发

posted on 2013-03-05 23:21  西瓜SY  阅读(656)  评论(0编辑  收藏  举报