jQuery.access源碼分析


基本理解

jQuery.attr是jQuery.attr,jQuery.prop,jQuery.css提供底層支持,jQuery里一個比較有特色的地方就是函數的重載, 比如attr,有如下幾種重載

  • $('#box').attr('title')
  • $('#box').attr('title','標題')
  • $('#box').attr({title:'標題',data-menu-toggle:'dropdown'})
  • $('#box').attr('title',function () {....})

但是縱觀jQuery.attr代碼中,卻沒有判斷value是不是存在啊之類的,ok,你猜對了,在access里實現了

jQuery.fn.extend({
    attr: function (name, value) {
        return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1);
    },

    removeAttr: function (name) {
        return this.each(function () {
            jQuery.removeAttr(this, name);
        });
    },

    prop: function (name, value) {
        return jQuery.access(this, jQuery.prop, name, value, arguments.length > 1);
    }
});

 

源碼分析

大致思路

      1.首先判斷key值是不是一個object,如果是,遍歷key,遞歸調用jQuery.access,並將是否可以鏈式調用的標志位設置為true
      2.判斷value值是否已經定義,如果已經定義,說明是個set操作
        2.1 set操作的是可鏈式調用的
        2.2 如果value不是function,設置raw為true
        2.3 判斷key值是否為null或者undefined,key若為空值
          2.3.1 如果value不是個函數,或者強制賦值raw為true,那么調用fn,可能是以下調用:$('#box').attr(null,{abc:'def',a:'1'})
          2.3.1 如果value是個函數,將fn包裝之,改變原來fn的作用域和參數
        2.4 如果fn存在,遍歷jQuery內部元素,分別執行set操作
      3.首先判斷是否為set方法,如果是,返回 elems,如果不是執行get操作(如果jQuery內部length為0,返回指定的默認空值)

源碼

    // Multifunctional method to get and set values of a collection
    // The value/s can optionally be executed if it's a function
    /**
    attr: function (name, value) {
        return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1);
    },
     *
     * @param elems jQuery的this
     * @param fn 函數
     * @param key 屬性
     * @param value 值
     * @param chainable 是否可以鏈式調用,如果是get動作,為false,如果是set動作,為true
     * @param emptyGet 如果jQuery沒有選中到元素的返回值
     * @param raw value是否為原始數據,如果raw是true,說明value是原始數據,如果是false,說明raw是個函數
     * @returns {*}
     */
    access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
        var i = 0,
            length = elems.length,
            bulk = key == null; // bulk 體積,容量;大多數,大部分;大塊

        // Sets many values
        /**
         * 如果參數key是對象,表示要設置多個屬性,則遍歷參數key,遍歷調用access方法
         *
         * $('#box').attr({data:1,def:'addd'});
         */
        if ( jQuery.type( key ) === "object" ) {
            chainable = true; //表示可以鏈式調用
            for ( i in key ) {
                jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
            }

        // Sets one value

            /**
             * $('#box').attr('customvalue','abc')
             * $('#box').attr('customvalue',function (value) {});
             */

        } else if ( value !== undefined ) {
            chainable = true;

            if ( !jQuery.isFunction( value ) ) {
                raw = true;
            }

            if ( bulk ) { // if (key == null && value !== undefined)
                // Bulk operations run against the entire set
                /**
                 * $('#box').attr(undefined,'abc')
                 *
                 * jQuery.attr.call(elems,value); 調用完畢之后,將fn設置為空
                 */
                if ( raw ) {
                    fn.call( elems, value );
                    fn = null;

                // ...except when executing function values
                    /**
                     * $('#box').attr(undefined,function () {})
                     *
                     * fn = bulk = jQuery.attr;
                     *
                     * fn = function (elem, key, value) {
                     *  return jQuery.attr.call(jQuery(elem),value);
                     * }
                     *
                     */
                } else { //如果key有值的話,好辦,這里的bulk是為了節省一個變量,將fn用bulk存起來,然后封裝fn的調用
                    bulk = fn;
                    fn = function( elem, key, value ) {
                        return bulk.call( jQuery( elem ), value );
                    };
                }
            }

            //jQuery.access(elems,jQuery.attr,)


            //如果fn存在,掉調用每一個元素,無論key是否有值,都會走到這個判斷,執行set動作
            if ( fn ) { // 遞歸調用之
                for ( ; i < length; i++ ) {
                    fn( elems[i], key,
                        raw ? value :
                        /**
                         * 如果value是原始數據,就取value,如果是個函數,就調用這個函數取值
                         * $('#box').attr('abc',function (index,value) { index指向當前元素的索引,value指向oldValue
                         *
                         *  先調用jQuery.attr(elements[i],key) 取到當前的值,然后調用傳入的fn值
                         * });
                         */


                            value.call( elems[i], i, fn( elems[i], key ) )
                    );
                }
            }
        }

        /**
         * 如果chainable為true,說明是個set方法,就返回elems
         * 否則說明是get方法
         * 1.如果bulk是個true,說明沒有key值,調用fn,將elems傳進去
         * 2.如果bulk為false,說明key有值哦,然后判斷元素的長度是否大於0
         *    2.1 如果大於0,調用fn,傳入elems[0]和key,完成get
         *    2.2 如果為0,說明傳參有問題,返回指定的空值emptyGet
         */
        return chainable ?
            elems :

            // Gets
            bulk ?
                fn.call( elems ) :
                length ? fn( elems[0], key ) : emptyGet;
    },

 

ExtJS 中的flexSetter方法

這讓我猛然想起了Ext的flexSetter方法,該方法在Ext.Function.flexSetter 詳細API請看這里

用法:

    var ele = document.getElementById('box');

    function setAttribute(name, value) {
        ele.setAttribute(name, value);
    }

    var flexSetAttribute = Ext.Function.flexSetter(setAttribute);

    flexSetAttribute('title', '標題');
    flexSetAttribute({
        'abc': 'otherattribu',
        'other': 1
    });

 

源碼

    /**
     * 1.關於Ext.enumerables的實現
     *
     * 這里為了兼容某些瀏覽器的toString,valueOf等內置方法不能被遍歷出來的bug
     * var o = {toString:111,valueOf:222};
     *
     * for (var oo in o)
     * {
           alert(oo);
       }
     *
     */
    var enumerables = [//'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
                'valueOf', 'toLocaleString', 'toString', 'constructor'];

    for (i in { toString: 1 }) {
        enumerables = null;
    }

    Ext.Function = {
        flexSetter: function(setter) {
            return function(name, value) { // 返回一個閉包
                var k, i;

                if (name !== null) {
                    if (typeof name !== 'string') { //如果name不是字符串,這里就認為是對象,進行for in
                        for (k in name) {
                            if (name.hasOwnProperty(k)) {
                                setter.call(this, k, name[k]); //逐個調用setter
                            }
                        }

                        if (Ext.enumerables) {
                            for (i = Ext.enumerables.length; i--;) {
                                k = Ext.enumerables[i];
                                if (name.hasOwnProperty(k)) {
                                    setter.call(this, k, name[k]);
                                }
                            }
                        }
                    } else {
                        setter.call(this, name, value); // 如果是個字符串,直接調用了
                    }
                }

                return this;
            };
        }
    };

 

總結:很顯然,Ext的flexSetter沒有jQuery的強大,但是足夠嚴謹,jQuery.access為prop、attr等上層需要靈活設置參數的功能做了一個統一的整理,方便了調用,節省了比特。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM