• 原文地址: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. Nodejs学习笔记(四)——支持Mongodb

    前言:回顾前面零零碎碎写的三篇挂着Nodejs学习笔记的文章,着实有点名不副实,当然,这篇可能还是要继续走着离主线越走越远的路子,从简短的介绍什么是Nodejs,到如何寻找一个可以调试的Nodejs ...

  2. 15个来自 CodePen 的酷炫 CSS 动画效果【下篇】

    CodePen 是一个在线的前端代码编辑和展示网站,能够编写代码并即时预览效果.你在上面可以在线分享自己的 Web 作品,也可以欣赏到世界各地的优秀开发者在网页中实现的各种令人惊奇的效果. 今天这篇文 ...

  3. Hyperledger fabric Client Node.js Hello World示例程序

    简介 Hyperledger fabric Client (HFC)提供了基于Node.js的应用接口来访问Hyperledger区块. 本文介绍了一个使用HFC访问IBM Bluemixr区块服务的 ...

  4. CSS布局:水平居中

    前言 一直对CSS布局一知半解,这段时间打算定下心来好好学习一下,于是先从最简单的水平居中布局开始入手.下面以分页组件为实例来记录各种实现方式. common.css <style type=& ...

  5. JS实现简易的计算器

    JS可以做的事多了,那就用来实现一个计算器吧 看看手机中的计算器,分为普通计算器和科学计算器     自认脑袋不够大,就实现一个普通版本的吧(支持正负数加减乘除等基本连续的运算,未提供括号功能) 看看 ...

  6. 一个有意思的js小问题

    问题:如何实现以下函数? add(2, 5); // 7 add(2)(5); // 7 第一个就不用说了,很简单,关键是看第二个,add(2)(5),可见add(2)应该返回的是一个函数,这个函数再 ...

  7. 【转载】CSS Sticky Footer: 完美的CSS绝对底部

    下面是我找到的一个比较完美的方法,来自国外的设计达人,纯CSS,可以实现: 当正文内容很少时,底部位于窗口最下面.当改变窗口高度时,不会出现重叠问题. <div id="wrap&qu ...

  8. Web API应用架构在Winform混合框架中的应用(3)--Winfrom界面调用WebAPI的过程分解

    最近一直在整合WebAPI.Winform界面.手机短信.微信公众号.企业号等功能,希望把它构建成一个大的应用平台,把我所有的产品线完美连接起来,同时也在探索.攻克更多的技术问题,并抽空写写博客,把相 ...

  9. PHP访问MySql数据库介绍

    在网站后台,经常要与数据库打交道.本文介绍如何使用XAMPP来管理MySql数据库及如何用PHP来访问MySql数据库. 一.使用XAMPP来管理MySql数据库 首先使用XAMPP打开MySql的管 ...

  10. 关于C#基础

    前几天帮人做个社交网站,还是用的控件方式,不过学习了ajax和一般处理程序ashx后,也用在了里面一些,今天回来继续写博客.继续上次总结下基础知识,学的内容多,总结的可能比较杂乱,分条总结为平时能自己 ...