• 原文地址:http://www.it165.net/pro/html/201404/11922.html

      内存泄露

      首先看看什么是内存泄露,这里直接拿来Aaron中的这部分来说明什么是内存泄露,内存泄露的3种情况:

      1 循环引用

      2 Javascript闭包

      3 DOM插入顺序

      在这里我们只解释第一种情况,因为jquery的数据缓存就是解决这类的内存泄露的。一个DOM对象被一个Javascript对象引用,与此同时又引用同一个或其它的Javascript对象,这个DOM对象可能会引发内存泄漏。这个DOM对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用DOM元素的对象或DOM对象的引用需要被赋值为null。

      含有DOM对象的循环引用将导致大部分当前主流浏览器内存泄露

      第一种:多个对象循环引用

      1.var a=new Object;
      2. 
      3.var b=new Object;
      4. 
      5.a.r=b;
      6. 
      7.b.r=a;

      第二种:循环引用自己

      1.var a=new Object;
      2. 
      3.a.r=a;

      循环引用很常见且大部分情况下是无害的,但当参与循环引用的对象中有DOM对象或者ActiveX对象时,循环引用将导致内存泄露。

      我们把例子中的任何一个new Object替换成document.getElementById或者document.createElement就会发生内存泄露了。

      在实际应用中我们要给我们的DOM添加数据,如果我们给一个DOM添加的数据太多的话,会存在循环引用的风险,例如我们添加的数据恰好引用了这个DOM元素,就会存在内存的泄露。所以jquery使用了数据缓存的机制就解决或者说避免这一问题。

      数据缓存

      $.cache 是jquery的缓存对象,这个是对象就是一个json,它的结构是这样的

      1.{ 'uid1': { // DOM节点1缓存数据,
      2.        'name1': value1,
      3.        'name2': value2
      4.    },
      5.    'uid2': { // DOM节点2缓存数据,
      6.        'name1': value1,
      7.        'name2': value2
      8.    }

      数据缓存的接口是

      $.data( element, key, value )

      $(selector).data(key,value)

      用法

      看代码之前,先看看怎么使用jquery的数据缓存。在jquery中,有两个方法可以给对象设置数据,分别是实例方法$().data()和静态方法$.data(),具体的使用过程大家看api就知道了,这里简单介绍下

      静态方法$.data()有三个参数,分别是挂在数据的元素,挂载的数据键,挂载数据的值,根据参数的不同,无非就是设置数据,取数据,具体如下

      1 $.data( elem, key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题
       2 $.data( elem, key ) 返回指定元素上name指定的值
       3 $.data( elem ) 返回全部数据
       4 $.data( elem,obj ) 在指定的元素上绑定obj

      01.var obj = {};
      02.$.data(obj , 'a' , 1);//普通对象添加数据
      03.console.log($.data(obj,'a'));//1
      04.var dom = $('body');//dom添加数据
      05.$.data(dom,'a',1)
      06.console.log($.data(dom,'a'));//1
      07.$.data(obj , {'b':2});//两个参数 绑定数据对象
      08.console.log($.data(dom,'b'));//2
      09.console.log($.data(dom));//1 2

      静态方法$().data()有两个参数,挂载的数据键,挂载数据的值

      1 $(selector).data( key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题
       2 $(selector).data( key ) 返回指定元素上name指定的值
       3 $(selector).data(obj ) 在指定的元素上绑定obj 
       4 $(selector).data() 返回全部数据

      1.$('body').data('a' , 1);//添加数据
      2.console.log($('body').data('a'));//1
      3.$('body').data({'b':2});//两个参数 绑定数据对象
      4.console.log($('body').data('b'));//2
      5.console.log($('body').data();//1 2

      思路

      回想下我们要解决什么问题:我们想在DOM上添加数据,但是不想引起内存的泄露,也就是我们不想引起循环引用,要尽量减少在DOM上挂数据。jquery的思路是这样:使用一个数据缓存对象$.cache,在需要绑定数据的DOM上扩展一个expando属性,这个属性存的是一个id,这里不会存在循环引用的情况了,之后将数据存在$.cache[id]上,当我们取DOM上的数据的时候,我们可以根据DOM上的expando找到id,进而找到存在$.cache[id]上的数据。可以看出jquery只是在DOM上扩展了一个属性expando,数据都存在了$.cache中,利用expando这个属性建立DOM和缓存对象之间的联系。无论我们添加多少的数据都会存储在缓存对象中,而不是直接挂在DOM上。这个唯一id是一个整型值,初始为0,调用data接口时自动加一,唯一id附加在以$.expando命名的属性上,$.expando是动态生成的,类似于一个时间戳,以尽可能的避免与用户变量冲突。从匹配的DOM元素上取到唯一id,在$.cache中找到唯一id对应的对象,再从对应的对象中找到key对应的值

      看例子,在源码里打断点看一下

      1.$.data($('body')[0],{'a':1});
      2.console.log($.data($('body')[0],'a'));

      DOM对象扩展了一个属性,这个属性存的是cache的id。

      这样大家就比较明显了。

      实现

      expando就是一个类似时间戳的东东,源码

      1.expando: 'jQuery' + ( jQuery.fn.jquery + Math.random() ).replace( /D/g, '' )

      就是为了生成标识的,没啥可说的。

      这是静态方法的代码的整体结构,我看到的1.10.2,变化较大,所有的方法的实现都封装成了函数,主要看 internalData( elem, name, data )这个函数,其他的大伙自己看看吧

      01.jQuery.extend({
      02.    cache: {},
      03. 
      04.    // The following elements throw uncatchable exceptions if you
      05.    // attempt to add expando properties to them.
      06.    noData: {
      07.        'applet': true,
      08.        'embed': true,
      09.        // Ban all objects except for <a href="http://www.it165.net/design/wfl/" target="_blank" class="keylink">Flash</a> (which handle expandos)
      10.        'object': 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
      11.    },
      12. 
      13.    hasData: function( elem ) {
      14.        elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
      15.        return !!elem && !isEmptyDataObject( elem );
      16.    },
      17. 
      18.    data: function( elem, name, data ) {
      19.        return internalData( elem, name, data );
      20.    },
      21. 
      22.    removeData: function( elem, name ) {
      23.        return internalRemoveData( elem, name );
      24.    },
      25. 
      26.    // For internal use only.
      27.    _data: function( elem, name, data ) {
      28.        return internalData( elem, name, data, true );
      29.    },
      30. 
      31.    _removeData: function( elem, name ) {
      32.        return internalRemoveData( elem, name, true );
      33.    },
      34. 
      35.    // A method for determining if a DOM node can handle the data expando
      36.    acceptData: function( elem ) {
      37.        // Do not set data on non-element because it will not be cleared (#8335).
      38.        if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
      39.            return false;
      40.        }
      41. 
      42.        var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
      43. 
      44.        // nodes accept data unless otherwise specified; rejection can be conditional
      45.        return !noData || noData !== true && elem.getAttribute('classid') === noData;
      46.    }
      47.});
      01.function internalData( elem, name, data, pvt /* Internal Use Only */ ){
      02.    if ( !jQuery.acceptData( elem ) ) {//查看是否可以接受数据
      03.        return;
      04.    }
      05.    var ret, thisCache,
      06.        internalKey = jQuery.expando,//jQuery副本的唯一标识
      07.        // We have to handle DOM nodes and JS objects differently because IE6-7
      08.        // can't GC object references properly across the DOM-JS boundary
      09.        isNode = elem.nodeType,//判断DOM节点
      10.        // Only DOM nodes need the global jQuery cache; JS object data is
      11.        // attached directly to the object so GC can occur automatically
      12.        cache = isNode ? jQuery.cache : elem,//若是是DOM对象,则cache就是$.cache,否则为参数elem对象
      13.        // Only defining an ID for JS objects if its cache already exists allows
      14.        // the code to shortcut on the same path as a DOM node with no cache
      15.        id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;//找id,id可能在DOM[expando]中,也可以在elem[expando]中
      16.    // Avoid doing any more work than we need to when trying to get data on an
      17.    // object that has no data at all
      18.    if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === 'string' ) {
      19.        return;//参数的一些判断限制
      20.    }
      21.    if ( !id ) {//id不存在
      22.        // Only DOM nodes need a new unique ID for each element since their data
      23.        // ends up in the global cache
      24.        if ( isNode ) {//是DOM节点
      25.            id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;//生成一个id
      26.        } else {//不是DOM,是一个对象
      27.            id = internalKey;//那么id就是那个expando
      28.        }
      29.    }
      30.    if ( !cache[ id ] ) {//cache中不存在数据,先弄成空的,一会在填充
      31.        // Avoid exposing jQuery metadata on plain JS objects when the object
      32.        // is serialized using JSON.stringify
      33.        cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
      34.    }
      35.    // An object can be passed to jQuery.data instead of a key/value pair; this gets
      36.    // shallow copied over onto the existing cache
      37.    if ( typeof name === 'object' || typeof name === 'function' ) {//处理第二个参数时对象或者是函数的情况
      38.        if ( pvt ) {//不太懂
      39.            cache[ id ] = jQuery.extend( cache[ id ], name );
      40.        } else {//添加到data属性上
      41.            cache[ id ].data = jQuery.extend( cache[ id ].data, name );
      42.        }
      43.    }
      44.    thisCache = cache[ id ];
      45.    // jQuery data() is stored in a separate object inside the object's internal data
      46.    // cache in order to avoid key collisions between internal data and user-defined
      47.    // data.
      48.    if ( !pvt ) {
      49.        if ( !thisCache.data ) {
      50.            thisCache.data = {};
      51.        }
      52.        thisCache = thisCache.data;
      53.    }
      54.    if ( data !== undefined ) {//第三个参数存在,就是存数据
      55.        thisCache[ jQuery.camelCase( name ) ] = data;
      56.    }
      57.    // Check for both converted-to-camel and non-converted data property names
      58.    // If a data property was specified
      59.    if ( typeof name === 'string' ) {
      60. 
      61.        // First Try to find as-is property data
      62.        ret = thisCache[ name ];//取出来待返回的那个value
      63.                //有啥用 这么麻烦
      64.        // Test for null|undefined property data
      65.        if ( ret == null ) {
      66.            // Try to find the camelCased property
      67.            ret = thisCache[ jQuery.camelCase( name ) ];
      68.        }
      69.    } else {
      70.        ret = thisCache;//就是返回存进来的那个对象或者函数
      71.    }
      72.    return ret;
      73.}

      实现起来还是比较简单的,只是有些地方jquery考虑的太周全了,我等凡人看不太透彻。

      pS:给DOM对象添加的数据是存储在了$.cache中,而给对象添加书数据直接挂在了对象的expando上面。其实给一个对象挂数据也没有什么实际的意义。

      看源码可以知道,看个例子更明显

      1.var obj = {};
      2.$.data(obj,{'a':1});
      3.console.log($.data(obj,'a'));
      4.console.log(obj);

      结果:

      实例方法data()其实就是调用了$.data()这个静态方法,这里就不说了。

      01.jQuery.fn.extend({
      02.    data: function( key, value ) {
      03.        var attrs, name,
      04.            data = null,
      05.            i = 0,
      06.            elem = this[0];
      07. 
      08.        // Special expections of .data basically thwart jQuery.access,
      09.        // so implement the relevant behavior ourselves
      10. 
      11.        // Gets all values
      12.        if ( key === undefined ) {
      13.            if ( this.length ) {
      14.                data = jQuery.data( elem );
      15. 
      16.                if ( elem.nodeType === 1 && !jQuery._data( elem, 'parsedAttrs' ) ) {
      17.                    attrs = elem.attributes;
      18.                    for ( ; i < attrs.length; i++ ) {
      19.                        name = attrs[i].name;
      20. 
      21.                        if ( name.indexOf('data-') === 0 ) {
      22.                            name = jQuery.camelCase( name.slice(5) );
      23. 
      24.                            dataAttr( elem, name, data[ name ] );
      25.                        }
      26.                    }
      27.                    jQuery._data( elem, 'parsedAttrs', true );
      28.                }
      29.            }
      30. 
      31.            return data;
      32.        }
      33. 
      34.        // Sets multiple values
      35.        if ( typeof key === 'object' ) {
      36.            return this.each(function() {
      37.                jQuery.data( this, key );
      38.            });
      39.        }
      40. 
      41.        return arguments.length > 1 ?
      42. 
      43.            // Sets one value
      44.            this.each(function() {
      45.                jQuery.data( this, key, value );//这是重点
      46.            }) :
      47. 
      48.            // Gets one value
      49.            // Try to fetch any internally stored data first
      50.            elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
      51.    },

      问题

      现在我们利用源码分析一些问题

      01.var a = $('body');
      02.var b = $('body');
      03.a.data('a',1);
      04.b.data('a',2);
      05.console.log(a.data('a'));//2
      06.console.log(b.data('a'));//2
      07. 
      08.$.data(a,'b',1);
      09.$.data(b,'b',2);
      10.console.log($.data(a,'b'))//1
      11.console.log($.data(b,'b'))//2
      12. 
      13.$.data(a[0],'b',1);
      14.$.data(b[0],'b',2);
      15.console.log($.data(a[0],'b'));//2
      16.console.log($.data(b[0],'b'));//2

      看着有些晕,先看下这个

      1.var a = $('body');
      2.var b = $('body');
      3.console.log(a[0] == b[0]);//true
      4.console.log(a == b);//false
      5.console.log( $('body') == $('body'));//false

      每一次$('body')都生成一个新的对象,所以每一次都会不同,$('body')[0]都是指向同一个body对象,a 和b指向的每个新对象的地址,所以不同。

      看第一组

      1.var a = $('body');
      2.var b = $('body');
      3.a.data('a',1);
      4.b.data('a',2);
      5.console.log(a.data('a'));//2
      6.console.log(b.data('a'));//2

      在看源代码这句

      1.this.each(function() {
      2.                jQuery.data( this, key, value );
      3.            })

      调用$.data(),但是这里第一个参数为this,是原生的DOM对象,第一组中的a和b的DOM对象都是body,所以添加数据会产生覆盖现象。

      第二组和第二组是正常情况,不解释了。

      小结

      这就是我的理解,希望大家指正。以后会多分析jquery的实现过程,源码的细节太难了。

//

转:jQuery.data的更多相关文章

  1. jQuery data

    大家会如何设计一个缓存呢? 一个简单的Cache (function(){ var __cache = {}, Cache = { get: function(__name){ return __ca ...

  2. jQuery.Data源码

    jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储原理 这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cach ...

  3. jQuery源码解读 - 数据缓存系统:jQuery.data

    jQuery在1.2后引入jQuery.data(数据缓存系统),主要的作用是让一组自定义的数据可以DOM元素相关联——浅显的说:就是让一个对象和一组数据一对一的关联. 一组和Element相关的数据 ...

  4. JQuery data API实现代码分析

    JQuery data 接口是什么? .data() Store arbitrary data associated with the matched elements or return the v ...

  5. HTML5 自定义属性 data-* 和 jQuery.data 详解

    新的HTML5标准允许你在普通的元素标签里,嵌入类似data-*的属性,来实现一些简单数据的存取.它的数量不受限制,并且也能由javascript动态修改,也支持CSS选择器进行样式设置.这使得dat ...

  6. jquery data方法

    jquery.data()文档:http://api.jquery.com/jQuery.data/ html5有个data-*属性,跟这个功能一样. Note: This is a low-leve ...

  7. jquery data方法取值与js attr取值的区别

    <a data-v="3"></a> jquery data方法的运行机制: 第一次查找dom,使用attributes获取到dom节点值,并将其值存到缓存 ...

  8. jQuery.data的是jQuery的数据缓存系统

    jQuery.Data源码 jQuery.data的是jQuery的数据缓存系统 jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储 ...

  9. 读jQuery源码 jQuery.data

    var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, n ...

随机推荐

  1. 【Java基础】方法

    Num1:检查参数的有效性 绝大多数的方法和构造器对于传递给它们的参数值都会有某些限制.比如:索引值必须是非负数,对象引用不能为null等等.这些都很常见,你应该在文档中清楚地指明所有这些限制,并在方 ...

  2. JAVA 设计模式 策略模式

    用途 Title 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 策略模式是一种行为型模式. 结构

  3. MFC抓网页

    CString chinachar_str("读取的东西:"); CInternetSession sion(NULL,); CHttpFile *http=NULL; CStri ...

  4. [译]学习IPython进行交互式计算和数据可视化(三)

    第二章 在本章中,我们将详细学习IPython相对以Python控制台带来的多种改进.特别的,我们将会进行下面的几个任务: 从IPython中使用系统shell以在shell和Python之间进行强大 ...

  5. 8.Fluent API in Code-First【Code-First系列】

    在前面的章节中,我们已经看到了各种不同的数据注解特性.现在我们来学习一下Fluent API. Fluent API是另外一种配置领域类的方式,它提供了更多的配置相比数据注解特性. Mappings[ ...

  6. C#控件及常用设计整

    C#控件及常用设计整 1.窗体    1 2.Label 控件    3 3.TextBox 控件    4 4.RichTextBox控件    5 5.NumericUpDown 控件    7 ...

  7. Android 获取可靠的手机编码

    项目中出现了将设备和用户信息进行绑定的需求.最先想到的是IMEI串码和IMSI串码.手机登陆的时候一直都没有问题.换了一个平板中之后IMEI和IMSI串码都获取不到了.后来查了一下原因,是因为平板上是 ...

  8. 交换排序---快速排序算法(Javascript版)

    快速排序是对冒泡排序的一种改进.通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行 ...

  9. ASP.NET中Request.RawUrl、Request.Url的区别

    如果访问的地址是: http://hovertree.com/guestbook/addmessage.aspx?key=hovertree%3C&n=myslider#zonemenu 那么 ...

  10. Redis系列四之复制

    一.复制基本配置与演示 为了避免单点故障,Redis提供了复制功能,可以实现自动同步的过程. 1.配置 同步后的数据分为两类:一类是主数据库(master),一类是从数据库(slave).主数据库可以 ...