zoukankan      html  css  js  c++  java
  • 读jQuery源码 jQuery.data

    var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
        rmultiDash = /([A-Z])/g;
    
    function internalData( elem, name, data, pvt /* Internal Use Only */ ){
        
        //如果elem元素不能附加值,退出
        if ( !jQuery.acceptData( elem ) ) {
            return;
        }
    
        // ## form core.js
        // Unique for each copy of jQuery on the page
        // Non-digits removed to match rinlinejQuery
        // expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
        // 每个Jquery都有单独的一个expando
        
        var ret, thisCache,
            internalKey = jQuery.expando,
    
            // We have to handle DOM nodes and JS objects differently because IE6-7
            // can't GC object references properly across the DOM-JS boundary
            // 由于IE6,7不能自动回收再DOM上的引用,所以要对DOM和JS对象区别对待
            isNode = elem.nodeType,
    
            // Only DOM nodes need the global jQuery cache; JS object data is
            // attached directly to the object so GC can occur automatically
            // 只有DOM需要全局的缓存变量,对象就直接插入值了
            cache = isNode ? jQuery.cache : elem,
    
            // Only defining an ID for JS objects if its cache already exists allows
            // the code to shortcut on the same path as a DOM node with no cache
            id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
            
            // 什么时候会出现id为undefined的时候?
            // internalData(document.createElement('div'),'namea',...)
            // internalData({});
    
        // Avoid doing any more work than we need to when trying to get data on an
        // object that has no data at all
        if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
            return;
        }
        
        // 如果有name而没有data,说明这是一个读取的动作
        // 但是没有得到id,获取cache[id]中啥都没有,在获取用户数据的时候cache[id].data啥都木有,就返回空
    
        if ( !id ) {
            // Only DOM nodes need a new unique ID for each element since their data
            // ends up in the global cache
            // 如果是DOM元素,则取得一个唯一的ID值
            if ( isNode ) {
                id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; 
            } else {
                id = internalKey; //这是jQuery.expando
            }
        }
    
        if ( !cache[ id ] ) {
            // Avoid exposing jQuery metadata on plain JS objects when the object
            // is serialized using JSON.stringify
            // 避免只用JSON.stringify(cache[id]) 所带来的循环引用
            cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
        }
    
        // An object can be passed to jQuery.data instead of a key/value pair; this gets
        // shallow copied over onto the existing cache
        // 也可以穿一个jQuery.data,浅拷贝过来
        
        // jQuery.data({},{name:'hhstuhacker',age:31})
        // cache[id] = {data:{name:'hhstuhacker',age:31}};
        
        // cache[ id ]存入的是jQuery的内部数据
        // cache[ id ].data 存入的是用户数据
        // 若果pvt为真,就拷贝到cache[ id ]里
        // 若果pvt为false,就拷贝到cache[ id ].data 里
        
        if ( typeof name === "object" || typeof name === "function" ) {
            if ( pvt ) {
                cache[ id ] = jQuery.extend( cache[ id ], name ); //直接拷贝,内部使用
            } else {
                cache[ id ].data = jQuery.extend( cache[ id ].data, name ); //直接拷贝data
            }
        }
    
        thisCache = cache[ id ];
    
        // jQuery data() is stored in a separate object inside the object's internal data
        // cache in order to avoid key collisions between internal data and user-defined
        // data.
        
        // jQuery.cache = {jQuery.expando: {data:{}}};
        // 这里是为了防止内部数据跟用户定义的数据项混淆,内部数据定义在jQ.cache里,用户定义数据在jQ.cache.[id] 里
        
        if ( !pvt ) {
            if ( !thisCache.data ) {
                thisCache.data = {};
            }
    
            thisCache = thisCache.data;
        }
    
        // 分情况了,如果传入了键值对,就设置之
        // 将camelCase驼峰
        if ( data !== undefined ) {
            thisCache[ jQuery.camelCase( name ) ] = data;
        }
    
        // Check for both converted-to-camel and non-converted data property names
        // If a data property was specified
        // 不管是否读取还是设置缓存,都会返回数据
        // 先尝试直接获取,如果获取不成功,就尝试驼峰式获取
        // 如果连name都没有指定,就获取整个thisCache对象
        if ( typeof name === "string" ) {
    
            // First Try to find as-is property data
            ret = thisCache[ name ];
    
            // Test for null|undefined property data
            if ( ret == null ) {
    
                // Try to find the camelCased property
                ret = thisCache[ jQuery.camelCase( name ) ];
            }
        } else {
            ret = thisCache;
        }
    
        return ret;
    }
    
    function internalRemoveData( elem, name, pvt ) {
        if ( !jQuery.acceptData( elem ) ) { //如果elem元素不能附加值,退出
            return; 
        }
    
        var thisCache, i,
            isNode = elem.nodeType,
    
            // See jQuery.data for more information
            cache = isNode ? jQuery.cache : elem,
            id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
    
        // If there is already no cache entry for this object, there is no
        // purpose in continuing
        // 如果没有得到cache[ id ] 那就不进行了
        if ( !cache[ id ] ) {
            return;
        }
    
        if ( name ) {
    
            thisCache = pvt ? cache[ id ] : cache[ id ].data;
    
            if ( thisCache ) {
    
                // Support array or space separated string names for data keys
                // $.removeData(element,['title','age','createTime']);
                // $.removeData(element,'title age createTime');
                if ( !jQuery.isArray( name ) ) {
    
                    // try the string as a key before any manipulation
                    if ( name in thisCache ) {
                        name = [ name ];
                    } else {
    
                        // split the camel cased version by spaces unless a key with the spaces exists
                        // 检测是否驼峰式在其中,如果不在,将其split
                        name = jQuery.camelCase( name );
                        if ( name in thisCache ) {
                            name = [ name ];
                        } else {
                            name = name.split(" ");
                        }
                    }
                } else {
                    // If "name" is an array of keys...
                    // When data is initially created, via ("key", "val") signature,
                    // keys will be converted to camelCase.
                    // Since there is no way to tell _how_ a key was added, remove
                    // both plain key and camelCase key. #12786
                    // This will only penalize the array argument path.
                    name = name.concat( jQuery.map( name, jQuery.camelCase ) );
                    
                    // 将不驼峰和驼峰的全部删除
                }
    
                //删除之
                i = name.length;
                while ( i-- ) {
                    delete thisCache[ name[i] ];
                }
    
                // If there is no data left in the cache, we want to continue
                // and let the cache object itself get destroyed
                
                // 如果说删除过后,cache[id]或者cache[id].data 都是空了,那么咱们需要清理下一步吧?
                if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
                    return;
                }
            }
        }
    
        // See jQuery.data for more information
        // 如果是用户使用的话,清空cache[id].data
        // 如果cache [ id ] 不是空的话,结束该函数
        // 如果cahce [ id ] 是空的话,还是下一步的清理行动啊
        if ( !pvt ) {
            delete cache[ id ].data;
    
            // Don't destroy the parent cache unless the internal data object
            // had been the only thing left in it
            if ( !isEmptyDataObject( cache[ id ] ) ) {
                return;
            }
        }
    
        // window.window === window;
        // isWindow: function (o) {return o.window === window;}
        // cache != cache.window 表示不是window
        
        // Destroy the cache
        // 如果是个DOM节点,那么cleanData
        if ( isNode ) {
            jQuery.cleanData( [ elem ], true );
    
        // ses: http://www.cnblogs.com/enein/archive/2012/08/23/2651312.html
        // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
        // 经测试,在IE6,7,8下面,var div = document.createElement('div') delete div.test;
        // 是报错的 而 jQuery.support.deleteExpando 正是这种检测
        } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
            delete cache[ id ];
    
        // When all else fails, null
        } else {
            cache[ id ] = null;
        }
    }
    
    jQuery.extend({
        cache: {},
    
        // The following elements throw uncatchable exceptions if you
        // attempt to add expando properties to them.
        // 这几个元素不能有附加值
        noData: {
            "applet": true,
            "embed": true,
            // Ban all objects except for Flash (which handle expandos)
            "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
        },
    
        hasData: function( elem ) {
            elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
            return !!elem && !isEmptyDataObject( elem );
        },
    
        data: function( elem, name, data ) {
            return internalData( elem, name, data );
        },
    
        removeData: function( elem, name ) {
            return internalRemoveData( elem, name );
        },
    
        // For internal use only.
        // 内部适用,这里设置pvt为true,返回内部数据,定位到cache[id]这一层
        _data: function( elem, name, data ) {
            return internalData( elem, name, data, true );
        },
    
        // 内部适用,这里设置pvt为true,返回内部数据,定位到cache[id]这一层
        _removeData: function( elem, name ) {
            return internalRemoveData( elem, name, true );
        },
    
        // A method for determining if a DOM node can handle the data expando
        acceptData: function( elem ) {
            // Do not set data on non-element because it will not be cleared (#8335).
            // 如果是个dom节点,并且不是element也不是document,那就表示不能被附加数据
            // see:http://www.cnblogs.com/hhstuhacker/p/NodeType.html
            if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
                return false;
            }
    
            var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
    
            // nodes accept data unless otherwise specified; rejection can be conditional
            // 如果是embed或者applet,或者是flash元素,就返回false
            // about falsh:: http://www.w3help.org/zh-cn/causes/HO8001
            return !noData || noData !== true && elem.getAttribute("classid") === noData;
        }
    });
    
    jQuery.fn.extend({
        data: function( key, value ) {
            var attrs, name,
                data = null,
                i = 0,
                elem = this[0];
    
            // Special expections of .data basically thwart jQuery.access,
            // so implement the relevant behavior ourselves
    
            // Gets all values
            // 如果key为undefined,那就表示要取得所有的data
            if ( key === undefined ) {
                if ( this.length ) { 
                    data = jQuery.data( elem ); //默认取第一个
    
                    // 看看cache内部是否已经解析过该DOM了,parsedAttrs
                    // 如果没有,得到属性列表
                    
                    if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
                        attrs = elem.attributes;
                        for ( ; i < attrs.length; i++ ) {
                            name = attrs[i].name;
    
                            if ( name.indexOf("data-") === 0 ) {
                                name = jQuery.camelCase( name.slice(5) );
    
                                //这里的data[ name ]为空值
                                dataAttr( elem, name, data[ name ] );
                            }
                        }
                        
                        //标记已经解析过了
                        jQuery._data( elem, "parsedAttrs", true );
                    }
                }
    
                return data;
            }
    
            // Sets multiple values
            // 如果是个对象,递归调用之
            if ( typeof key === "object" ) {
                return this.each(function() {
                    jQuery.data( this, key );
                });
            }
    
            return arguments.length > 1 ?
    
                // Sets one value
                this.each(function() {
                    jQuery.data( this, key, value );
                }) :
    
                //$('.color-box').data('someone','new');
                
                // Gets one value
                // Try to fetch any internally stored data first
                elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
                
                //$('#box').data('dataFirst');
                
                // <div data-data-first="something....."></div>
        },
    
        removeData: function( key ) {
            return this.each(function() {
                jQuery.removeData( this, key );
            });
        }
    });
    
    function dataAttr( elem, key, data ) {
        // If nothing was found internally, try to fetch any
        // data from the HTML5 data-* attribute
        //这个函数,主要是处理HTML5的问题
        if ( data === undefined && elem.nodeType === 1 ) {
    
            // 驼峰反转
            var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
    
            data = elem.getAttribute( name );
            
            // rbrace 对象或者数组正则
    
            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 ) ? jQuery.parseJSON( data ) :
                            data;
                } catch( e ) {}
    
                // Make sure we set the data so it isn't changed later
                jQuery.data( elem, key, data );
                
                //设置进去
    
            } else {
                data = undefined;
            }
        }
    
        return data;
    }
    
    // checks a cache object for emptiness
    function isEmptyDataObject( obj ) {
        var name;
        for ( name in obj ) {
    
            // if the public data object is empty, the private is still empty
            // 如果cache.data 为空,跳过本次循环,不判断了
            // 如果不跳过啊,就走到下面了吧,name !== data 杠杠的
            if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
                continue;
            }
            
            //toJSON是内置的,如果看到其他name,就不为空
            if ( name !== "toJSON" ) {
                return false;
            }
        }
    
        return true;
    }

    jQuery的版本为1.9.2

  • 相关阅读:
    vue开发chrome扩展,数据通过storage对象获取
    Vue手动集成less预编译器
    Google Translate寻找之旅
    Javascript Range对象的学习
    Javascript Promises学习
    SublimeText 建立构建Node js系统
    We're sorry but demo3 doesn't work properly without JavaScript enabled. Please enable it to continue.
    npm安装包出现UNMET DEPENDENCY报错
    (转载)命令行说明中格式 尖括号 中括号的含义
    Linux重启网卡服务Failed to start LSB: Bring up/down networking.
  • 原文地址:https://www.cnblogs.com/hhstuhacker/p/inside_jquery_source_data.html
Copyright © 2011-2022 走看看