jQuery数据赋值解析
来源:互联网 发布:淘宝网舞蹈水袖 编辑:程序博客网 时间:2024/06/06 03:14
作者:Jiang, Jinlin
在我们使用jQuery中,会用到$(element).data()方法存取赋值到元素上的数据。通过该方法,我们每次通过selector获取该元素时,总能获取其赋值的数据。今天,我们就来看看jQuery是如何实现的。
首先,我们先从data方法看起:
data方法接受两种参数形式,第一种通过key,value形式将数据赋值于元素之上。第二种通过key形式获取赋值的数据。(其中,如果key为空则导出所有key-value map)
通过data方法不但可以获取通过data本身传入的数据,也可以直接获取元素上直接[data-*]形式赋值的数据。接下来,就让我们看一看jQuery的源代码实现(我会在代码中进行标注,当然如果你不愿意看细节,之后会有一个图示来标示jQuery的数据赋值):jQuery.fn.extend({
data: function( key, value ) {
var i, name, data,
elem = this[0],
// 如果存在元素则获取它的attributes属性
attrs = elem && elem.attributes;
// Special expections of .data basically thwart jQuery.access,
// so implement the relevant behavior ourselves
// Gets all values
// 如果没有设定key,则将元素包含数据全部导出
if ( key === undefined ) {
if ( this.length ) {
// 获取元素数据,之后会进入分析
data = jQuery.data( elem );
// nodeType属性用于获取类型,其中1为DOM元素类型。因而其只会返回dom元素赋值的数据
// _data为内部使用方法,之后将会详细介绍
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
i = attrs.length;
while ( i-- ) {
// Support: IE11+
// The attrs elements can be null (#14894)
if ( attrs[ i ] ) {
name = attrs[ i ].name;
// 检查是否存在以data-开头的属性
if ( name.indexOf( "data-" ) === 0 ) {
// 使用驼峰法表示属性名,例如data-user-mail,将会被转成userMail
name = jQuery.camelCase( name.slice(5) );
// 获取[data-*]值并赋值数据
dataAttr( elem, name, data[ name ] );
}
}
}
// 标示该元素已经获取了[data-*]值,之后添加的data-*属性将不再获取
// 你可以进行如下代码尝试一下结果:
// $(ele).attr("data-test", 123);
// $(ele).data(); => {test: 123}
// $(ele).attr("data-test", 321);
// $(ele).data(); => {test: 123}
jQuery._data( elem, "parsedAttrs", true );
}
}
return data;
}
// 如果传入的是一个object对象,则将其包含的所有key-value传入
// 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 );
}) :
// 返回赋值数值
// Gets one value
// Try to fetch any internally stored data first
elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
},
});
jQuery的data与_data方法调用的都是调用内部的internalData方法,只是参数不同:
jQuery.extend({
cache: {},
data: function( elem, name, data ) {
return internalData( elem, name, data );
},
// For internal use only.
_data: function( elem, name, data ) {
return internalData( elem, name, data, true);
},
});
而dataAttr方法:
function dataAttr( elem, key, data ) {
// 如果内部没有发现数据,则尝试获取html5的[data-*]属性的数值
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
// 将驼峰法转换成data-*-*形式
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
// 对string值进行类型转换,所以如果值符合转换条件就会被转掉。
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
// 将该值存入元素的data缓存
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
接着就是internalData,逻辑比较复杂。在此之前,我们先看一下相关的函数:
// jQuery初始化时,会创建一个随机的key值,这个值将会用于元素的内置jQuery mapping
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
// 可以接受数据赋值的mapping表
noData: {
"applet ": true,
"embed ": true,
// ...but Flash objects (which have this classid) *can* handle expandos
"object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
},
/**
* Determines whether an object can have data
*/
jQuery.acceptData = function( elem ) {
// 检查元素类型是否接受数据赋值
var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
// 获取nodeType,如果没有(js object等)则将其认为是dom元素
nodeType = +elem.nodeType || 1;
// 对元素类型进行判断,dom元素或者document或者flash object可以接受数据赋值
// Do not set data on non-element DOM nodes because it will not be cleared (#8335).
return nodeType !== 1 && nodeType !== 9 ?
false :
// Nodes accept data unless otherwise specified; rejection can be conditional
!noData || noData !== true && elem.getAttribute("classid") === noData;
};
然后是internalData:
function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
// 元素是否接受数据赋值(见上)
if ( !jQuery.acceptData( elem ) ) {
return;
}
var ret, thisCache,
internalKey = jQuery.expando,//见上
// jQuery会分开处理js object和dom元素的数据赋值(因为IE 6-7有GC bug)
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
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元素则使用jQuery的cache( jQuery使用global cache来保存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,如果不是dom元素则直接以internalKey作为id
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
// 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;
}
// 如果没有id则赋予id
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
// jQuery内置了一个deletedIds数组用于存储被弃用的id,如果有弃用的id则会被复用
id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
} else {
id = internalKey;
}
}
// 如果没有cache则创建。对于js object,会添加一个toJSON的空方法以阻住JSON.stringify将容器内数据一同转换
if ( !cache[ id ] ) {
// Avoid exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
}
// 允许直接传入一个object将其数据添加到cache
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
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 );
}
}
thisCache = cache[ id ];
// jQuery的缓存结构:{data: {}},用户存储的数据会被存于data中,从而避免和jQuery保存的数据冲突
// 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.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
// 保存数据,同样会使用驼峰法将key改写
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
// 获取数据,会返回原key的值,如果没有则返回驼峰法后key的值。如果key不是string类型,直接返回所有赋值数据
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
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;
}
我们看一下简化的流程图:
插入单个key,value
插入key-value 集合
按key获取value
获取全部key-value集合
看过了jQuery的数据赋值思路后,我们也可以实现一个简单的版本:
var cache = [];
var _innerID = 0;
function data(ele, key, value) {
if(!ele._innerID) {
ele._innerID = ++_innerID;
}
var _cache = cache[ele._innerID] = cache[ele._innerID] || {};
if(key === undefined) {
return _cache;
} else if(value === undefined) {
return _cache[key];
} else {
_cache[key] = value;
}
}
总结
jQuery数据赋值的实现分成dom元素和非dom元素两种。两者除了缓存容器选取不同外,存取逻辑是公用的。其中对于dom元素,会额外处理一次html5的[data-*]数据赋值。但是就如前所说的,当data-*数据被存入缓存容器后,jQuery便不会再次处理。因而通过data-*更新的数据将不会被$.fn.data获取到。
因而,我建议除非由于页面初始化赋值,否则应该尽量避免动态调整[data-*]赋值,而转用jQuery的data方法存取赋值。
- jQuery数据赋值解析
- jquery解析json数据
- jquery解析json数据
- jquery解析json数据
- Jquery解析json数据
- jquery解析JSON数据
- jquery 解析 xml数据
- Jquery解析JSON数据
- jquery解析json数据
- Jquery解析json数据
- jquery解析json数据
- jQuery解析JSON数据
- JQuery解析XML数据
- jQuery 解析 JSON 数据
- jquery解析JSON数据
- jquery赋值
- Jquery解析Json格式数据
- 使用jQuery解析JSON数据
- 自学Swift-斯坦福笔记整理(十一) Unwind segue alert
- Mysql的引擎
- [LeetCode-27] Remove Element(数组元素删除)
- C#中ComboBox的SelectedIndexChanged事件获取Tag值
- access and faccessat 按照实际用户Id和实际组ID进行文件权限测试
- jQuery数据赋值解析
- 华为oj【大数求和】
- IAP
- Android使用fragment底部菜单栏
- iOS学习笔记: 初步探索KVC & KVO
- IBM 关于 Java 深入学习的技术资料
- Linux 下curl模拟Http 的get or post请求。
- 圆形的imageview,常用来显示头像
- MFC窗口风格 说明及在c#中的定义使用