data数据缓存 3308
先做个区分:
下面这三种方式,都可以弹出hello。
attr使用方法。
$('#content-box').attr('name', 'hello');
alert($('#content-box').attr('name')); //hello
document.getElementById('#content-box').setAttribute('name', 'hello');
alert(document.getElementById('#content-box').getAttribute('name'));
prop使用方法.
$('#content-box').prop('name', 'hello');
alert($('#content-box').prop('name')); //hello
document.getElementById('#content-box').name = 'hello';
alert(document.getElementById('#content-box').name);
[] 和 . 是一样的。
document.getElementById('#content-box')['name'] = 'hello';
alert(document.getElementById('#content-box')['name']);
上面的两种方法,不适合做大量数据,适合做简单数据或者元素本身的属性。设定一个class,id等。
但是要加载很多数据的时候,就用data挂载大量的数据。
data使用方法.
$('#content-box').data('name', 'hello');
alert($('#content-box').data('name')); //hello
讨论一下JS内存泄漏的问题。
变量不用的时候,浏览器有自动的垃圾回收机制,去销毁掉。这个变量就消失了。内存释放。
JS有几种方式可以引起内存泄漏的。
其中一种就是dom元素与对象之间互相引用。大部分浏览器就会出现内存泄漏。
DOM是如何与对象间产生内存泄漏的。
举个例子:
html
<div id='test' class='content-box'></div>
JS
var oDiv = document.getElementById('test');
var obj = {};
oDiv.name = obj;
obj.age = oDiv;
解释一下:oDiv是一个DOM的对象,而obj又是一个JS的对象。
首先,给oDiv添加了一个name属性,赋值为obj,
然后,又用obj去引用oDiv。就出现了内存泄漏。
// 这里选择器先选出了一个DMO对象,然后可能会对其中的属性,进行加载对象操作。
$('#content-box').attr('name', obj);
// 有可能未来的某一行代码,就出现了obj去引用DOM的可能,那就出现了内存泄漏的这个坑。
解决问题的方式,就是用data。
就不用担心内存泄漏的问题,data会防止DOM和对象之间的相互引用,防止内存泄漏。
$('#content-box').data('name', obj);
/*****************************************************/
设计思路:
data设计了一个中介,将DOM与数据(对象),间接的联系在一起。
通过cache = {}, 来进行连接,做了一个映射。
比如:
$('#box1').attr('XXX', obj1);
$('#box2').attr('YYY', obj2);
$('#box3').attr('ZZZ', obj3);
由于选择器一定是有先后顺序的,所以,为box1的XXX属性设定一个1.
box2的YYY属性设定一个2,box3的ZZZ属性设定一个3。都存入cache的对象中。
此时:cache = {}----->>>>
转变为----->>>>>
cache = {
1: {
XXX : obj1
},
2: {
YYY : obj2
},
3: {
ZZZ : obj3
}
}
而HTML中的属性----->>>>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
转变为----->>>>>
<div id="box1" XXX='1'></div>
<div id="box2" YYY='2'></div>
<div id="box3" ZZZ='3'></div>
这样, XXX可以通过1,来找到cache对象中的响应的属性值。
[XXX ---> 1]
[YYY ---> 2]
[ZZZ ---> 3]
利用这种方式,可以防止内存泄漏。这回将数据存入Json中,也就是cache中。
跟DOM就没关系了。因为dom中存放的是一个字符串,也就是键值对的形式。反正不是对象。
通过cache建立了链接,给dom上面挂载数据。
jQuery的小习惯。
$('div').html('hello'); // 可以设定一堆
但是,
alert($('div').html()); // 只返回一个。
/*****************************************************/
结构:
Data.prototype = {
key // 分配映射的。类似有搞定键值对。
set // 设定
get // 获取
access // get/set的集合方法。2个参数,用get,三个参数set
remove // 删除cache中元素
hasData // 判断是否存在
discard // 删除cache中苏醒
}
/*****************************************************/
结构:
jQuery.extend({
acceptData // 可以接受元素的范围
hasData Data->hasData // 判断是否有这个数据,有就返回真,没有返回假。
data Data->access // 绑定数据,获取,设定参数。
removeData Data->remove // 删除数据
_data // 这是私有方法。
_removeData // 这是私有方法。内部使用
});
这里的Data是,上面的对象。
使用例子:
extend的方法,既可以给原生的js用,也可以给jQuery对象用。
$.data(document.body, 'age', 22);
alert($.data(document.body, 'age')); //22
alert($.hasData(document.body, 'age')); // true
$.removeData(document.body, 'age');
alert($.data(document.body, 'age')); //undefined
alert($.hasData(document.body, 'age')); // false
$.data(document.body, {'hg', 'wj'}); //这样也可以,里面可以处理json数据格式
/*****************************************************/
结构:
jQuery.fn.extend({
data
removeData
})
使用例子:
$('#test').data('name', 'hgwj'); // 绑定数据
alert($('#test').data('name')); // hgwj
$('#test').removeData('name'); // 删除数据
alert($('#test').data('name')); // undefined
/***********************************************************************************/
阅读源码:
var data_user, data_priv,
rbrace = /(?:{[sS]*}|[[sS]*])$/,
rmultiDash = /([A-Z])/g;
function Data() {
// Support: Android < 4,
// Old WebKit does not have Object.preventExtensions/freeze method,
// return new empty object instead with no [[set]] accessor
// 英文介绍,老式的webkit不支持这个方法。所以选择用这个defineProperty来代替。
// preventExtensions 和 freeze 作用就是防止对象被修改。
// Android < 4 不支持。ES5的方法defineProperty
Object.defineProperty( this.cache = {}, 0, {
get: function() {
return {};
}
});
// 返回一个jQuery的版本 +随机数 + 随机数的组合。
// 重复的概率很低,
this.expando = jQuery.expando + Math.random();
console.log(this.expando); //jQuery203083968277710742660.6855781189593687
console.log(this);
// this 对应下面一拖数据。 有一些属性和方法,已经键值对说对应的值。
/*
Data {cache: Object, expando: "jQuery203077120183012448250.8953614917118102"}
cache: Object0: (...)get 0: ()arguments: nullcaller: nulllength: 0name: ""prototype: Object.defineProperty.get__proto__: ()<function scope>1: Objectage: 22__proto__: Object__proto__: Object__defineGetter__: __defineGetter__()__defineSetter__: __defineSetter__()__lookupGetter__: __lookupGetter__()__lookupSetter__: __lookupSetter__()constructor: Object()hasOwnProperty: hasOwnProperty()isPrototypeOf: isPrototypeOf()propertyIsEnumerable: propertyIsEnumerable()toLocaleString: toLocaleString()toString: toString()valueOf: valueOf()get __proto__: get __proto__()set __proto__: set __proto__()expando: "jQuery203077120183012448250.8953614917118102"__proto__: Object
jquery-2.0.3.js:3323 Data {cache: Object, expando: "jQuery203077120183012448250.46686112554743886"}cache: Objectexpando: "jQuery203077120183012448250.46686112554743886"__proto__: Object
*/
}
Data.uid = 1;
Data.accepts = function( owner ) {
// Accepts only:
// - Node
// - Node.ELEMENT_NODE
// - Node.DOCUMENT_NODE
// - Object
// - Any
// 这里是jQuery中DOM选择器选出来的是DOM节点,如果是1 是节点, 9 是文档,
return owner.nodeType ?
owner.nodeType === 1 || owner.nodeType === 9 : true;
};
Data.prototype = {
key: function( owner ) {
// 判断有没有元素,没有就返回0.
if ( !Data.accepts( owner ) ) {
return 0;
}
var descriptor = {},
// Check if the owner object already has a cache key
// 看是否是第一次进来,如果是第一次,就证明没有分配过 this.expando
// unlock就是undefiend,如果分配过,证明这个元素已经不是第一次分配数据了。
unlock = owner[ this.expando ];
// If not, create one
// 没有创建过 this.expando 属性,就创建,并且添加入节点中。
if ( !unlock ) {
unlock = Data.uid++;
// 这里就让用户无法改变的 this.expando 这个属性,
try {
descriptor[ this.expando ] = { value: unlock };
// 这个方法就是只能获取,不能修改。
Object.defineProperties( owner, descriptor );
// 但是这里安卓4以下无法使用,就做一个兼容写法。
} catch ( e ) {
descriptor[ this.expando ] = unlock;
// 传统的方式添加。
jQuery.extend( owner, descriptor ); //将数据添加到传入的参数中。
}
}
// Ensure the cache object
if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};
}
return unlock;
},
// 设定数据。
set: function( owner, data, value ) {
var prop,
unlock = this.key( owner ),
cache = this.cache[ unlock ];
// Handle: [ owner, key, value ] args
if ( typeof data === "string" ) {
cache[ data ] = value;
// Handle: [ owner, { properties } ] args
} else { //Json 的形式。
// 这里说明:$.data(document.body, {'hg', 'wj'});
// 这样也可以,里面可以处理json数据格式
// Fresh assignments by object are shallow copied
if ( jQuery.isEmptyObject( cache ) ) {
jQuery.extend( this.cache[ unlock ], data );
// Otherwise, copy the properties one-by-one to the cache object
} else {
for ( prop in data ) {
cache[ prop ] = data[ prop ];
}
}
}
return cache;
},
//获取
get: function( owner, key ) {
// 跟设定一样,都是通过这个key去找到id。
var cache = this.cache[ this.key( owner ) ];
return key === undefined ?
cache : cache[ key ];
},
// 根据参数的个数,整合一下 get 和 set
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) ) {
stored = this.get( owner, key );
return stored !== undefined ?
stored : this.get( owner, jQuery.camelCase(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
//
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;
},
// 删除。
remove: function( owner, key ) {
var i, name, camel,
unlock = this.key( owner ),
cache = this.cache[ unlock ];
// 不存在key的时候,就全部都干掉。
if ( key === undefined ) {
this.cache[ unlock ] = {};
}
// 有key的时候。
else {
// Support array or space separated string of keys
if ( jQuery.isArray( key ) ) {
// 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.
// 这里返回一个驼峰的写法
// 也就是说这里的 (hg-wj === hgWj),这两个是等价的。
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 {
// If a key with the spaces exists, use it.
// Otherwise, create an array by matching non-whitespace
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 ] ] || {}
);
},
// 删除的是一个整体, 把这个 this.expando 给干掉了。
discard: function( owner ) {
if ( owner[ this.expando ] ) {
delete this.cache[ owner[ this.expando ] ];
}
}
};
// These may be used throughout the jQuery core codebase
data_user = new Data();
data_priv = new Data();
例子: (1)
解释一下 defineProperty 和 freeze
Object.freeze(obj);这个函数,导致了obj无法被修改,只能阅读。
var obj = {name: 'hg'};
obj.name = 'wj';
console.log(obj.name); //wj
Object.freeze(obj);
obj.name = 'hg';
console.log(obj.name); //还是wj
Object.defineProperty(obj);
/*三个参数。
(1) 要锁定的对象,
(2) 建立了 一个 对应索引值。
0 对应了一个索引 var obj = {name: 'hg', 0 : {123} };
给obj对象添加了一个key 和 返回值 value 。
这么写,因为没有set方法,只能get方法,不能设置,只能获取。
(3) 操作对象数据。
*/
var obj = {name: 'hg'};
obj.name = 'wj';
Object.defineProperty(obj, 0, {
get : function () {
return {0:123};
},
});
console.log(obj[0]); //Object {0: 123}
obj[0] = 123;
console.log(obj[0]); //Object {0: 123} 并没有改变
// es5 中还是有很多很不错的方法,可以玩玩。
/***********************************************************************************/
工具方法:调用的都是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 );
},
_data: function( elem, name, data ) {
return data_priv.access( elem, name, data );
},
_removeData: function( elem, name ) {
data_priv.remove( elem, name );
}
});
/***********************************************************************************/
实例方法。
jQuery.fn.extend({
data: function( key, value ) {
var attrs, name,
// 获取this
elem = this[ 0 ],
i = 0,
data = null;
// Gets all values
// 判断key是否为空
if ( key === undefined ) {
// $('dsd') --> 获取不到
if ( this.length ) { // 判断元素的长度。是否有元素
data = data_user.get( elem );
// 这里一并判断了自定义的属性。也兼容html5中的自定义属性。
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
attrs = elem.attributes; // 获取所有属性的一个集合。
for ( ; i < attrs.length; i++ ) {
name = attrs[ i ].name;
// 判断数据中是否包含 data-这种,给转驼峰。
if ( name.indexOf( "data-" ) === 0 ) {
name = jQuery.camelCase( name.slice(5) );
// 设定到cache中。
dataAttr( elem, name, data[ name ] );
}
}
data_priv.set( elem, "hasDataAttrs", true );
}
}
return data;
}
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 );
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;
}
data = data_user.get( elem, camelKey );
if ( data !== undefined ) {
return data;
}
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() {
data_user.set( this, camelKey, value );
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 );
});
}
});
例子:
(1)
$('#div1').data('name', 'hg');
$('#div1').data('age', '21');
// 会打印所有的数据, {'name':'hg', 'age':'21'};
console.log($('#div1').data());
(2)
自定义数据是h5的特性。
<div id='hgwj' data-hgwj='hgwjtest'></div>
alert($('#hgwj').get(0).dataset.hgwj); // 打印出来 hgwjtest
<div id='hgwj' data-hgwj-all='hgwjtest'></div>
alert($('#hgwj').get(0).dataset.hgwjAll); // 打印出来 hgwjtest
(3)
如果存入的是这两个,那么jQuery会认为是不同的。
$('#div1').data('nameAge', 'h1');
$('#div1').data('name-age', 'hellow');
this.cache = {
1 : {
nameAge' : 'hello',
'name-age' : 'hello'
}
}