• https://www.cnblogs.com/yuanjun1/p/4001953.html
    网上有很多教你怎么使用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]) 源代码后可以知道:

<mark>每一个 element 都会有自己的一个 {key:value} 对象保存着数据</mark>
所以新建的对象就算有 key 相同它也不会覆盖原来存在的对象 key 所对应的 value
因为新对象保存是是在另一个 {key:value} 对象中。

省略原文 each 介绍 …

再来看看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="#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]) 的源代码不是很了解,好吧,我就用一个例子来模仿实现它吧。

<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>  

总结

  • jQuery.data(element,[key],[value]) 只把数据绑定到参数 element 节点上。
  • data([key],[value])$("div").data("a","aaaa") 它是把数据绑定每一个匹配 div节点 的元素上。

附加说明下
文中所分析用到的是jquery-1.7.2.js的源代码。如果你想了解更多请自行去下载