var dataPriv = new Data(); //以后会讲到 var dataUser = new Data(); jQuery.extend( { hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } } );
源码里面有 dataPriv 和 dataUser,作者做了一个 TODO 标记,
接着是 jQuery.fn.data() :
jQuery.fn.extend( { data: function( key, value ) { var i, name, data, // 将第一个 dom 赋给 elem elem = this[ 0 ], attrs = elem && elem.attributes; // key 为 underfined,表示参数空,获取全部 if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // 这里面从 dom 的 attribute 中搜索 data- 开通的属性 if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // object 类型 if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } // key value 的情况,利用 access 函数 return access( this, function( value ) { var data; // 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 // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // html5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { dataUser.remove( this, key ); } ); } } );data 函数略有不同,但思路也很清晰。
有几个要提一下的函数其中,有几个函数,也来介绍一下, acceptData :
var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); };
acceptData 是判断 owner 的类型,具体关于 nodeType,去看看 这里 吧。
jQuery.camelCase :
jQuery.camelCase = function (string) { return string.replace(/^-ms-/, "ms-").replace(/-([a-z])/g, function (all, letter) { return letter.toUpperCase(); }); }这个函数就是做了一些特殊字符串的 replace,具体有啥用,我也不是很清楚。
isEmptyObject 是判断一个 Object 是否为空的函数,挺有意思的,可以借鉴:
jQuery.isEmptyObject = function (obj) { var name; for (name in obj) { return false; } return true; }
dataAttr 是一个从 DOM 中搜索以 data- 开头属性的函数:
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( /[A-Z]/g, "-$&" ).toLowerCase(); // 利用 dom 自身的 get 操作 data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { // 先看有没有 data = getData( data ); } catch ( e ) {} // Make sure we set the data so it isn't changed later dataUser.set( elem, key, data ); } else { data = undefined; } } return data; } 总结jQuery 的 data 缓存从源码来看的话,真的不是很难,而且不难发现,jQuery 缓存的实质, 其实就是在内部先弄一个 Object,然后和缓存体(DOM)建立一对一的联系,所有增删改查的操作,都是围绕着 jQuery 内部来的,不直接对 DOM 操作,这样就可以避免内存泄漏 。而且从源码来看,jQuery 的缓存机制自带清内存操作,更是锦上添花呀。