• jQuery源码04 data() : 数据缓存


    /*
        Implementation Summary
    
        1. Enforce API surface and semantic compatibility with 1.9.x branch
        2. Improve the module's maintainability by reducing the storage
            paths to a single mechanism.
        3. Use the same single mechanism to support "private" and "user" data.
        4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
        5. Avoid exposing implementation details on user objects (eg. expando properties)
        6. Provide a clear path for implementation upgrade to WeakMap in 2014
    */
    var data_user, data_priv,
        rbrace = /(?:{[sS]*}|[[sS]*])$/,
        rmultiDash = /([A-Z])/g;
    
    function Data() {
        // cache = {0:{} },并且这个0属性是不能被修改的。这个0是公用的。
        Object.defineProperty( this.cache = {}, 0, {
            get: function() {
                return {};
            }
        });
        //唯一标识,id
        this.expando = jQuery.expando + Math.random();
    }
    /*
    var cache = {
        1 : {                //同一个元素只有一个id
            age : 30,
            job : 'it',
            'allName' : '妙味课堂'
        },
        2 : {
            age : obj
        }
    */
    Data.uid = 1;//cache中可以累加的属性1,2,3
    
    Data.accepts = function( owner ) {
        // 1是元素,2是document,都是可以在cache中分配id的。
        return owner.nodeType ?
            owner.nodeType === 1 || owner.nodeType === 9 : true;
    };
    
    Data.prototype = {
        //分配映射id或者0:空json
        key: function( owner ) {//owner是document.body或者$('#div1')对象,
            // 不是元素也不是document,就返回0,id就是0,在cache中返回空对象{},defineProperty中给cache定义的空对象。
            if ( !Data.accepts( owner ) ) {
                return 0;
            }
            var descriptor = {},
                // 获取$('#div1')的属性this.expando的值,
                unlock = owner[ this.expando ];
            if ( !unlock ) {
                unlock = Data.uid++;
                try {
                    /*
                    descriptor={jQuery203089541586732714850.8840931279098725:{value:1}}
                    */
                    descriptor[ this.expando ] = { value: unlock };
                    //给owner增加一个属性descriptor:json并且不能改变
                    Object.defineProperties( owner, descriptor );
    
                // Support: Android < 4
                // Fallback to a less secure definition
                } catch ( e ) {
                    //兼容性:通过extend给owner增加{key:value}的键值对,这个可以被修改descriptor={jQuery203089541586732714850.8840931279098725:1}
                    descriptor[ this.expando ] = unlock;
                    jQuery.extend( owner, descriptor );
                }
            }
            if ( !this.cache[ unlock ] ) {
                this.cache[ unlock ] = {};//cache={1:{}}
            }
            return unlock;
        },
        //设置cache的值
        set: function( owner, data, value ) {
            var prop,
                //找到这个元素的id和cache中的json
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];//cache是某个id的json
    
            if ( typeof data === "string" ) {
                cache[ data ] = value;//是字符串,就把key value加进去
            //是json,$.data(document.body ,{ 'age' : 30 , 'job' : 'it' , 'allName' : '妙味课堂'});
            } else {
                // Fresh assignments by object are shallow copied
                if ( jQuery.isEmptyObject( cache ) ) {
                    jQuery.extend( this.cache[ unlock ], data );//继承json过去
                // Otherwise, copy the properties one-by-one to the cache object
                } else {
                    for ( prop in data ) {
                        cache[ prop ] = data[ prop ];
                    }
                }
            }
            return cache;
        },
        //获取cache中的值
        get: function( owner, key ) {
            var cache = this.cache[ this.key( owner ) ];
    
            return key === undefined ?
                cache : cache[ key ];
        },
        //set,get合体
        access: function( owner, key, value ) {
            var stored;
            // In cases where either:
            //
            //   1. No key was specified
            //   2. A string key was specified, but no value provided
            //
            // Take the "read" path and allow the get method to determine
            // which value to return, respectively either:
            //
            //   1. The entire cache object
            //   2. The data stored at the key
            //
            if ( key === undefined ||
                    ((key && typeof key === "string") && value === undefined) ) {
           //没有key全部取出来,有key没有value取key
                stored = this.get( owner, key );
    
                return stored !== undefined ?
                    stored : this.get( owner, jQuery.camelCase(key) );
            }
          //有key有value.key是字符串全部覆盖,key是json追加
            // [*]When the key is not a string, or both a key and value
            // are specified, set or extend (existing objects) with either:
            //
            //   1. An object of properties
            //   2. A key and value
            //
            this.set( owner, key, value );
    
            // Since the "set" path can have two possible entry points
            // return the expected data based on which path was taken[*]
            return value !== undefined ? value : key;
        },
        
        //移除cache
        remove: function( owner, key ) {
            var i, name, camel,
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];//cache是某一个key的cache,
    
            if ( key === undefined ) {//不指定key,这个元素所有的都清空。
                this.cache[ unlock ] = {};
    
            } else {
                // $.removeData(document.body , ['age','job','all-name']);
                if ( jQuery.isArray( key ) ) {
                    //all-name找到allName
                    name = key.concat( key.map( jQuery.camelCase ) );
                } else {
                    camel = jQuery.camelCase( key );
                    // Try the string as a key before any manipulation
                    if ( key in cache ) {
                        name = [ key, camel ];
                    } else {
                        // 转驼峰,去空格后在不在
                        name = camel;
                        name = name in cache ?
                            [ name ] : ( name.match( core_rnotwhite ) || [] );
                    }
                }
    
                i = name.length;
                while ( i-- ) {
                    delete cache[ name[ i ] ];
                }
            }
        },
        hasData: function( owner ) {
            return !jQuery.isEmptyObject(
                this.cache[ owner[ this.expando ] ] || {}
            );
        },
        discard: function( owner ) {//删除1,2,这个整体
            if ( owner[ this.expando ] ) {
                delete this.cache[ owner[ this.expando ] ];
            }
        }
    };
    
    // These may be used throughout the jQuery core codebase
    data_user = new Data();//cache就是依附在这个对象上
    data_priv = new Data();
    
    //对外提供的接口,通过$.直接调用。这个是方法的定义,准备给jQuery类调用的,里面的实现都是调用Data对象的实现。
    jQuery.extend({
        acceptData: Data.accepts,
    
        hasData: function( elem ) {
            return data_user.hasData( elem ) || data_priv.hasData( elem );
        },
    
        data: function( elem, name, data ) {
            return data_user.access( elem, name, data );
        },
    
        removeData: function( elem, name ) {
            data_user.remove( elem, name );
        },
    
        // TODO: Now that all calls to _data and _removeData have been replaced
        // with direct calls to data_priv methods, these can be deprecated.
        //私有的,内部使用,不对外使用
        _data: function( elem, name, data ) {
            return data_priv.access( elem, name, data );
        },
    
        _removeData: function( elem, name ) {
            data_priv.remove( elem, name );
        }
    });
    
    //对外提供的接口,通过jQuery对象调用。设置一组元素时候是设置所有元素,获取元素只是获取第一个。
    jQuery.fn.extend({
        data: function( key, value ) {
            var attrs, name,
                elem = this[ 0 ],//如果是一组div,就只要第一个,
                i = 0,
                data = null;
    
            // Gets all values
            if ( key === undefined ) {
                if ( this.length ) {//$('#div1')找不到得到
                    data = data_user.get( elem );
    //alert($('#div1').get(0).dataset.miaovAll);//h5特性,data-miaov-all="妙味"
    //对h5特性的处理
                    if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
                        attrs = elem.attributes;//元素所有属性数组集合[title='123',class='box',data-miaov-all='苗圩',id='div1']
                        for ( ; i < attrs.length; i++ ) {
                            name = attrs[ i ].name;//属性的名字
    
                            if ( name.indexOf( "data-" ) === 0 ) {
                                //miaov-all转成miaoAll,js中不要出现横杆
                                name = jQuery.camelCase( name.slice(5) );
                                //存入cache
                                dataAttr( elem, name, data[ name ] );
                            }
                        }
                        data_priv.set( elem, "hasDataAttrs", true );
                    }
                }
    
                return data;
            }
    
            // Sets multiple    values$('#div1').data({name:'hello',age:'30'});
            if ( typeof key === "object" ) {
                return this.each(function() {
                    data_user.set( this, key );
                });
            }
    
            return jQuery.access( this, function( value ) {
                var data,
                    camelKey = jQuery.camelCase( key );
    
                // The calling jQuery object (element matches) is not empty
                // (and therefore has an element appears at this[ 0 ]) and the
                // `value` parameter was not undefined. An empty jQuery object
                // will result in `undefined` for elem = this[ 0 ] which will
                // throw an exception if an attempt to read a data cache is made.
                if ( elem && value === undefined ) {
                    // Attempt to get data from the cache
                    // with the key as-is
                    data = data_user.get( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // Attempt to get data from the cache
                    // with the key camelized
                    data = data_user.get( elem, camelKey );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // Attempt to "discover" the data in
                    // HTML5 custom data-* attrs
                    data = dataAttr( elem, camelKey, undefined );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // We tried really hard, but the data doesn't exist.
                    return;
                }
    
                // Set the data...
                this.each(function() {
                    // First, attempt to store a copy or reference of any
                    // data that might've been store with a camelCased key.
                    var data = data_user.get( this, camelKey );
    
                    // For HTML5 data-* attribute interop, we have to
                    // store property names with dashes in a camelCase form.
                    // This might not apply to all properties...*
                    data_user.set( this, camelKey, value );
    
                    // *... In the case of properties that might _actually_
                    // have dashes, we need to also store a copy of that
                    // unchanged property.
                    if ( key.indexOf("-") !== -1 && data !== undefined ) {
                        data_user.set( this, key, value );
                    }
                });
            }, null, value, arguments.length > 1, null, true );
        },
    
        removeData: function( key ) {
            return this.each(function() {
                data_user.remove( this, key );
            });
        }
    });
    
    function dataAttr( elem, key, data ) {
        var name;
    
        // If nothing was found internally, try to fetch any
        // data from the HTML5 data-* attribute
        if ( data === undefined && elem.nodeType === 1 ) {
            name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
            data = elem.getAttribute( name );
    
            if ( typeof data === "string" ) {
                try {
                    data = data === "true" ? true :
                        data === "false" ? false :
                        data === "null" ? null :
                        // Only convert to a number if it doesn't change the string
                        +data + "" === data ? +data :
                        rbrace.test( data ) ? JSON.parse( data ) :
                        data;
                } catch( e ) {}
    
                // Make sure we set the data so it isn't changed later
                data_user.set( elem, key, data );
            } else {
                data = undefined;
            }
        }
        return data;
    }
  • 相关阅读:
    在javascript中如何取消事件冒泡
    ThinkPHP与EasyUI整合之二(datagrid):删除多条记录
    Jquery动画效果地铁站名指示等效果
    ubuntu 10.4 setup vm tools log
    Windows下Critical Section、Event、Mutex、Semaphores区别
    联通GPRS卡在windows mobile操作系统手机上网如何设置
    hope DATA
    电动车电池正确的使用方法
    C语言运算符表
    深圳市职业技能鉴定报名
  • 原文地址:https://www.cnblogs.com/yaowen/p/6926957.html
Copyright © 2020-2023  润新知