51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

学习data([key],[value])和jQuery.data(element,[key],[value])

123.png

昨天一篇文章我们分享了一篇文章"你对jQuery.data()了解有多少",学习data()的一些知识,今天这篇文章我们进一步的学习,用它来实现数据缓存。

网上有很多教你怎么使用jQuery.data(..)来实现数据缓存,但有两个用户经常使用的data([key],[value])和jQuery.data(element,[key],[value]),很少说清楚它们的区别,在此我想大概的说明下:
$("").data([key],[value])与jQuery.data(element,[key],[value]),这两个函数都是用来在元素上存放数据也就平时所说的数据缓存,都返回jQuery对象,当时我分别在使用它俩的时候真的吓我一跳,区别可大了,真是不用不知道,一用吓一跳。看例子先吧,后再根据源代码分析。
代码如下:

<div id="test2" onclick="test()">test2</div> 
<div id="abc3" onclick="test()">test3</div> 
<div id="test" onclick="test()">test</div> 
<p id="ttt">aaaa</p> 
<script> 
    $(document).ready(function(){ 
    $("#test").click(function(){ 
    alert("JQUERY"); 
    var e=$("div");//定义了两jquery对象 
    var w=$("div");//e是不等于w的。     
    //首先使用data([key],[value])用法。 
    $(e).data("a","aaaa");//分别在e和w上保存Key一样的数据, 
    $(w).data("a","wwww");// 看它是否会覆盖前面的,虽然是保存在不同对象上。 
    alert($(e).data("a"));//你猜到答案了吗,里输出是wwww;是不是有点意外? 
    alert(e===w)//false 
    alert($(w).data("a"));//这里也是wwww; 
    
    //使用jQuery.data(element,[key],[value])来存放数据。 
    $.data(e,"b","cccc");//分别在e和w上保存Key一样的数据, 
    $.data(w,"b","dddd");// 看它是否会覆盖前面的,虽然是保存在不同对象上。 
    alert($.data(e,"b"));//应该你能猜答案吧,输出cccc 
    alert($.data(w,"b"));//这输出dddd 
    }); 
    }); 
</script>

看了上面的例子是不是发现data([key],[value])与jQuery.data(element,[key],[value])两个根本就不一样了对吧?它们之间到底有没有关系呢。怎么data([key],[value])会覆盖前面key相同的值呢?
而jQuery.data(element,[key],[value])只要是绑定到不同的对象上都不会造成覆盖。是这样吗?那来研究下它们的源代码吧。
先看jQuery.data(element,[key],[value])源代码。如下:

jQuery.extend({ 
cache: {}, 

// Please use with caution  uuid: 0, 

// Unique for each copy of jQuery on the page  // Non-digits removed to match rinlinejQuery  expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), 

....  data: function( elem, name, data, pvt /* Internal Use Only */ ) {  // 是否可以附加数据,不可以则直接返回  if ( !jQuery.acceptData( elem ) ) {  return;  } 

var privateCache, thisCache, ret,  //jQuery.expando这是一个唯一的字符串,是这介jquery对象产生的时候就生成了。  internalKey = jQuery.expando,  getByName = typeof name === "string", 

// 必须区分处理DOM元素和JS对象,因为IE6-7不能垃圾回收对象跨DOM对象和JS对象进行的引用属性  isNode = elem.nodeType, 

// 如果是DOM元素,则使用全局的jQuery.cache  // 如果是JS对象,则直接附加到对象上  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,  isEvents = name === "events"; 

// 避免做更多的不必要工作,当尝试在一个没有任何数据的对象上获取数据时  // 对象没有任何数据,直接返回  if  ( (!id || !cache[id] || (!isEvents && !pvt &&  !cache[id].data)) && getByName && data === undefined ) {  return;  }  // 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 ) {  // 如果是DOM元素则在元素上产生唯一的ID 并且以jQuery.expando  //为属性值为id保存在elem元素上,以便以后再根据jQuery.expando来查找ID。  elem[ internalKey ] = id = ++jQuery.uuid;  } else {  // JS对象则直接使用jQuery.expando,既然是直接附加到对象上,又何必要id呢?  // 避免与其他属性冲突!  id = internalKey;  }  } 

//// 当我们试着访问一个键是否含有值的时候,如果不存在jQuery.cache[id]值,  // 初始化jQuery.cache[id]值 为一个空对象{}  if ( !cache[ id ] ) {  cache[ id ] = {}; 

if ( !isNode ) {  cache[ id ].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  // 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 );  }  }  / 存储对象,存放了所有数据的映射对象  privateCache = 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内部数据存在一个独立的对象(thisCache.data==thisCache[ internalKey ])  //上,为了避免内部数据和用户定义数据冲突  if ( !pvt ) {  // 存放私有数据的对象不存在,则创建一个{}  if ( !thisCache.data ) {  thisCache.data = {};  }  // 使用私有数据对象替换thisCache  thisCache = thisCache.data;  }  // 如果data不是undefined,表示传入了data参数,则存储data到name属性上  if ( data !== undefined ) {  // jQuery.camelCase( name )作用是如果传入的是object/function,不做转换,  //只有传入的name是字符串才会转换。所以最终保存下来的是key/value对;  thisCache[ jQuery.camelCase( name ) ] = data;  } 

//从这以后下面的代码都是处理data: function( elem, name)data为空,求返回值data的情况了。 

if ( isEvents && !thisCache[ name ] ) {  return privateCache.events;  } 

// 如果name是字符串,则返回data  // 如果不是,则返回整个存储对象  if ( getByName ) { 

// 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;  },  ............  });

我们看张图。


看jQuery.data(element,[key],[value])源代码后可以知道,每一个element都会有自己的一个{key:value}对象保存着数据,所以新建的对象就算有key相同它也不会覆盖原来存在的对象key所对应的value,因为新对象保存是是在另一个{key:value}对象中。
接下来要分析data([key],[value])源代码使用到了each(callback),在分析它之前先看下each(callback)用法和源代码。
代码如下:

<div id="test2" onclick="test()">test2</div> 
<div id="abc3" onclick="test()">test3</div> 
<div id="test" onclick="test()">test</div> 
<p id="ttt">aaaa</p> 
<script> 
    $(document).ready(function(){ 
    $("#test").click(function(){ 
    alert("JQUERY"); 
    
    var i=0; 
    $("#abc3").each(function() { 
    alert(++i);//只输出1;因为只有一个<div id="abc3"> 
    }); 
    alert("----"); 
    var j=0; 
    $("div").each(function() { 
    alert(++j);//分别输出1,2,3;因为有三个<div>所以循环三遍 
    }); 
    }); 
    }); 
</script>

现在来看each方法的具体实现如下:

jQuery.fn = jQuery.prototype = { 
    each: function( callback, args ) { 
        return jQuery.each( this, callback, args ); 
    } 
}

可以看到它返回的是全局的each方法,并且将自身jQuery对象做为参数给它,全局的each方法的具体实现如下:

// args 作为内部成员的调用来使用 
each: function( object, callback, args ) { 
var name, i = 0, length = object.length; // 当object为jQuery对象时,length非空 

if ( args ) {  if ( length === undefined ) {  for ( name in object )  if ( callback.apply( object[ name ], args ) === false )  break;  } else  for ( ; i < length; )  if ( callback.apply( object[ i++ ], args ) === false )  break;  // 以下是客户端程序进行调用  } else {  if ( length === undefined ) {  for ( name in object )  if ( callback.call( object[ name ], name, object[ name ] ) === false )  break;  } else  // i表示索引值,value表示DOM元素  for ( var value = object[0];  i < length && callback.call( value, i, value ) !== false;  value = object[++i] ){}  } 

return object;  }

现在我们关注下 for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 这句代码;其中object[0]取得jQuery对象中的第一个DOM元素,通过for循环,
得到遍历整个jQuery对象中对应的每个DOM元素,通过callback.call( value,i,value); 将callback的this对象指向value对象,并且传递两个参数,i表示索引值,value表示DOM元素;其中callback是类似于 function(index, elem) { } 的方法。所以就得到 $("").each(function(index, elem){ });

再来看看data([key],[value])的源代码。

jQuery.fn.extend({ 
data: function( key, value ) { 
var parts, part, attr, name, l, 
elem = this[0], 
i = 0, 
data = null; 

// Gets all values  if ( key === undefined ) {  .....//处理没有Key的情况,这里不是我们要讨论的

return data;  } 

// Sets multiple values  if ( typeof key === "object" ) {  return this.each(function() {  jQuery.data( this, key );  });  } 

parts = key.split( ".", 2 );  parts[1] = parts[1] ? "." + parts[1] : "";  part = parts[1] + "!"; 

return jQuery.access( this, function( value ) { 

if ( value === undefined ) {  。。。//这里是没有value时,是索取返回值的情况,这不是我们讨论  } 

parts[1] = value; 

//如果我使用用$("div").data("a","aaa")),下面调用each前的this指的是$("div")这返回的对象,  this.each(function() {//注意了,这里是以每一个匹配的元素作为上下文来执行一个函数  var self = jQuery( this ); 

self.triggerHandler( "setData" + part, parts ); 

//这里在元素上存放数据,本质还是委托data(element,[key],[value])来做的。  //看前面有分析过了。  //下面data( this, key, value )里的this指的是遍历整个jQuery对象中对应的每个DOM元素  //$("div")它对应页面中一个<div>数组。  jQuery.data( this, key, value )<span style="background-color: #ffcc00;">;//这名句会被循环多次执行,也就是保存数据</span>。  //这里就是核心一句话。但要清楚看上面了它是在each(functipn(){})中的。  self.triggerHandler( "changeData" + part, parts );  });  }, null, value, arguments.length > 1, null, false );  },  //在元素上移除存放的数据。具体实现如下:  removeData: function( key ) {  return this.each(function() {  jQuery.removeData( this, key );  });  }  }); 

如果对于data([key],[value])的源代码不是很了解,好吧,我就用一个例子来模仿实现它吧。 Js代码:

复制代码 代码如下:

<div id="test2" onclick="test()">test2</div>  <div id="abc3" onclick="test()">test3</div>  <div id="test" onclick="test()">test</div>  <p id="ttt">aaaa</p>  <script>      $(document).ready(function(){      $("#test").click(function(){      alert("JQUERY");           var i=0;      $("#abc3").each(function() {      alert(++i);//只输出1;因为只有一个<div id="abc3">      });      alert("----");      var j=1;      $("div").each(function() {//以每一个匹配的元素作为上下文来执行这个函数           $.data(this,"a","wwww");//这里的this就是指$("div"),      //分别遍历每一个匹配的元素给它们每一个对象{}都保存一个key/value      alert(j++);//分别输出1 ,2 ,3 因为有三个<div>元素      });      alert($("#test").data("a"));//返回wwww,      //是不是很惊呀,我没有保存在它身上啊,怎么也有值,很明显是它是查这个div节点上有没有,      //肯定是有值了,因为上面给循环保存在div这Dom结点上了。      alert($("#test")===$("div"));//false证明两新建的对象不是同一个。           alert($("div").data("a"));//返回wwww,      //这里也是一样因为是div节点上都保存了"a"="wwww"这样一个键值对了。      });      });  </script>

现在对data([key],[value])与jQuery.data(element,[key],[value])都有了解了吧,如果还是半懂,再回头多看一遍,耐心地理解一下。其实表面上很不一样。但本质上还是有联系的,现在明白原理后就可以请放心地使用了。jQuery.data(element,[key],[value])只把数据绑定到参数element节点上。data([key],[value]),如$("div").data("a","aaaa")它是把数据绑定每一个匹配div节点的元素上。

总结

这些知识相对于刚入门的童鞋来说,可能有些懵,但是可以慢慢学习。

赞(3)
未经允许不得转载:工具盒子 » 学习data([key],[value])和jQuery.data(element,[key],[value])