// 代码行:3915——3972 // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( toType( key ) === "object" ) { chainable = true; for ( i in key ) { access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } } if ( chainable ) { return elems; } // Gets if ( bulk ) { return fn.call( elems ); } return len ? fn( elems[ 0 ], key ) : emptyGet; }; // 代码行:4004——4158 function Data() { // 设定唯一标识 this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; Data.prototype = { // 建立一个cache cache: function( owner ) { // Check if the owner object already has a cache // 检查所有者对象是否已经拥有缓存 var value = owner[ this.expando ]; // If not, create one // 如果没有value,则创建一个 if ( !value ) { value = {}; // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. // 我们可以在现代浏览器中接受非元素节点的数据,但是我们不应该这么做,总是返回一个空对象。 if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment //如果它是一个不太可能被字符串化或循环的节点,使用简单的赋值 // 判断owner是一个合格者后 if ( owner.nodeType ) { owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property // configurable must be true to allow the property to be // deleted when data is removed //否则,将其保护在一个不可枚举的属性中 //可配置的必须为true才能允许属性为true //删除数据时删除 } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true } ); } } } return value; }, // set()用于dom设置key和value set: function( owner, data, value ) { var prop, cache = this.cache( owner ); // Handle: [ owner, key, value ] args // Always use camelCase key (gh-2257) if ( typeof data === "string" ) { cache[ camelCase( data ) ] = value; // Handle: [ owner, { properties } ] args // 处理data为这种情况:[ owner, { properties } ] } else { // Copy the properties one-by-one to the cache object // 将属性逐个复制到缓存对象 for ( prop in data ) { cache[ camelCase( prop ) ] = data[ prop ]; } } return cache; }, get: function( owner, key ) { return key === undefined ? this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; }, // 用来访问,将get、set结合到一起,并对underfined判断 access: function( owner, key, value ) { // 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 // //在下列任何一种情况下:1.没有指定密钥。2.指定了字符串键,但没有提供值。选择“read”路径并允许get方法确定,分别返回哪个值:1.整个缓存对象 2.存储在键上的数据 // 如果key是undefined或key是字符串、data是undefined说明实在读取数据 if ( key === undefined || ( ( key && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key ); } // 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 // //当键不是字符串,或者键和值都不是时。指定、设置或扩展(现有对象)时,1.属性的对象。2.键和值 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[*] // 因为“set”路径可以有两个可能的入口点 // 返回所选择路径的期望数据[*] return value !== undefined ? value : key; }, // 用于移除cache remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; } if ( key !== undefined ) { // Support array or space separated string of keys // 支持删除数组格式的key if ( Array.isArray( key ) ) { // If key is an array of keys... // We always set camelCase keys, so remove that. key = key.map( camelCase ); } else { // 为了保持一致,强行的构造了一个数组 key = camelCase( key ); // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace //如果存在空格键,就使用它。否则,通过匹配非空格创建数组 key = key in cache ? [ key ] : ( key.match( rnothtmlwhite ) || [] ); } i = key.length; while ( i-- ) { delete cache[ key[ i ] ]; } } // Remove the expando if there's no more data // cache为空的时候,删除整个缓存 if ( key === undefined || jQuery.isEmptyObject( cache ) ) { // Support: Chrome <=35 - 45 // Webkit & Blink performance suffers when deleting properties // from DOM nodes, so set to undefined instead // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) if ( owner.nodeType ) { owner[ this.expando ] = undefined; } else { delete owner[ this.expando ]; } } }, hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); } }; var dataPriv = new Data(); var dataUser = new 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 // 代码行:4172——4221 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /[A-Z]/g; function getData( data ) { if ( data === "true" ) { return true; } if ( data === "false" ) { return false; } if ( data === "null" ) { return null; } // Only convert to a number if it doesn't change the string if ( data === +data + "" ) { return +data; } if ( rbrace.test( data ) ) { return JSON.parse( data ); } return data; } // 函数dataAttr(elem, key, data)解析HTML5属性data-中含有的数据,并把解析结果放入DOM元素关联的自定义数据缓存对象中。 function dataAttr( elem, key, data ) { // 参数elem:表示待解析HTML5属性data-的DOM元素。 // 参数key:表示待解析的数据名,不包含前缀data-。 // 参数data:表示从DOM元素关联的自定义数据缓存对象中取到的数据,只有参数data为undefined时,才会解析HTML5属性data-,即优先读取自定义数据缓存对象中的数据。 var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute // 如果参数data为undefined,即该DOM元素关联的自定义数据缓存对象中没有指定名称的数据,才会尝试从HTML5属性data-中解析数据,并把尝试把解析结果转换为合适的JavaScript类型,最后把解析结果放入该DOM元素关联的自定义数据缓存对象中。 if ( data === undefined && elem.nodeType === 1 ) { // 增加前缀“data-”,并把可能的驼峰式参数name转换为连字符式,然后调用方法elem.getAttribute(name)读取对应的HTML属性,把返回值赋值给变量data。方法elem.getAttribute(name)用于返回指定属性的字符串值。 name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); data = elem.getAttribute( name ); // 如果方法elem.getAttribute(name)有返回值,则尝试把字符串类型的返回值转换为合适的JavaScript类型,包括布尔值、null、数值、对象、数组。 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)把HTML5属性data-的解析结果放入关联的自定义数据缓存对象jQuery.cache[id].data中 dataUser.set( elem, key, data ); // 如果方法elem.getAttribute(name)的返回值不是字符串,即返回值时null,则修正为undefined。 } else { data = undefined; } } // 返回属性data-的解析结果。如果参数data不是undefined,则不会解析属性data-,而是直接返回参数data。 return data; } // 代码行:4247——4328 jQuery.fn.extend( { // 代码行:4248——4321 // 方法.data(key, value)用于为匹配元素设置或读取自定义数据,解析HTML5属性data-,并触发相应的自定义事件getData、setData、changeData。 data: function( key, value ) { // 参数key:表示要设置或读取的数据名,或者是含有键值对的对象。 // 参数value:表示要设置的数据值,可以是任意类型。 var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values // 如果为传入参数,即参数格式是.data(),则返回第一个匹配元素关联的自定义数据缓存(即获全部数据),包括HTML5属性data-中的数据。 if ( key === undefined ) { if ( this.length ) { // 调用方法dataUser.get()获取第一个匹配元素关联的自定义数据缓存对象,并返回。 data = dataUser.get( elem ); // 如果未解析第一个匹配元素的HTML5属性data-,则调用函数dataAttr()解析属性data-中含有的数据,并把解析结果放入关联的自定义数据缓存对象中。解析完成后会设置自定义数据hasDataAttrs为true,表示已经为该元素解析过属性data-。 if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE 11 only // The attrs elements can be null (#14894) // attrs元素可以为空 if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values // 如果参数key是对象,即参数格式是.data(obj),则遍历匹配元素集合,为每个匹配元素调用方法dataUser.set()批量设置数据。 if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } // 否则调用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 ); } } );
来源:https://www.cnblogs.com/lidongming/p/10943986.html