深入学习jQuery中的data()

大家应该都会有这样一种感觉,data函数在jQuery中看起来很不起眼, 就像沙滩上一颗平凡的沙子, 但仔细一瞅, 却惊讶的发现data是jQuery中无比重要的一环, 甚至jQuery中各种事件都基于此。下面就来详细深入的学习下jQuery中的data(),感兴趣的朋友们可以参考借鉴。

data有什么作用?

在我们平时js编码过程中,我们经常会向DOM元素中添加各种自定义属性,这样有一个弊端。

  1、假设我们在DOM元素中添加了一个属性,这个属性指向了某个js对象。 dom1.ele = jsObj

  2、当这个js对象发挥完作用后,我们已经用不到他了。这时候按理说应该把这个js变量清空,释放内存。大家都知道,如果一个js对象不存在任何外在引用的话,解释器会自动将其在内存中删除,这也是javascript相对于c++等手动管理内存的程序的优点。

  3、但是这时候问题来了,因为DOM元素引用了这个js对象,尽管这个js对象已经没有存在的意义了,但是解释器是不会把他删除的。如果想要把其删除,我们可能需要将DOM元素的这个属性设置为null。

  4、我们编写了这么多的代码,哪里能把 每个js对象是不是被DOM元素引用了都记住啊?

  5、而且,假如DOM元素与js对象之间相互循环引用,根本就无法删除! 这就是内存泄漏

  6、所以,为了避免这种情况的发生,我们要尽量避免 引用数据(这里的引用数据可以说是javascript对象) 直接依附在DOM对象上。

  7、data就是用来搞定以上问题的方法。

data是如何搞定以上问题的?

首先来说一说jQuery中Data实现的大体思路:

  1、首先我们创建一个数据缓存池,这个缓存池专门用来存储  向 DOM对象或者jQuery对象附加的额外数据。

  2、当我们要向DOM对象或者jQuery对象附加额外数据的时候,我们附加的数据其实是保存于这个缓存池中

  3、DOM对象或者jQuery对象生成一个额外属性,这个属性保存了 附加数据在缓存池中的‘门牌号'(位置或者索引)

  4、当我们访问DOM对象或者jQuery对象的附加数据时,实际上是先取得其附加数据的门牌号,然后找到缓存池中对应门牌号的数据,进行操作。

大体思路讲完,那么来分析一下具体思路:

在jQuery中,有一个Data构造函数,每当运行这个构造函数时,就会生成一个实例。

jQuery默认会自动生成两个Data实例:

  var dataPriv = new Data()   jQuery私有的,我们尽量不要对这个实例进行操作。

  var dataUser = new Data()   这个就是服务于用户了,我们使用data()方法都是对这个实例进行操作。

所有的Data实例都有以下属性:

  expando:  值为字符串类型,每个Data实例的expando属性的值都不相同,用来区分不同的Data实例,类似于id的作用,expando的值就是上文中的额外属性。

  uid:   这就是上文中的门牌号,初始为1,随着不同对象的附加数据的加入,自增长。

  cache : 一个对象 {} ,这就是缓存池了。

来个实例:

 $(document.body).data('aaa', 'value-aaa') console.dir(document.body)

body对象有一个名为jquer210023......的额外属性,

  这个属性的名称就是dataUser的expando的值

  这个属性的值就是门牌号。

总结: data实际上就是对js对象或者DOM对象的额外属性做了一个集中的管理。对于那些不会产生内存泄漏的额外数据,我们也可以直接向js对象或者DOM对象附加。

好,理清楚上面的关系后,我们再来看一下源码:

 define([ "../core", "../var/rnotwhite", "./accepts" ], function( jQuery, rnotwhite ) { function Data() { // Support: Android<4, // Old WebKit does not have Object.preventExtensions/freeze method, // return new empty object instead with no [[set]] accessor Object.defineProperty( this.cache = {}, 0, { get: function() { return {}; } }); // jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ) expando是一个jQuery的唯一标示 // 格式是:'jQuery\\d*' 也就是'jQuery'+ 多个数字。这里为啥要搞得这么麻烦呢? // 应因为我们可能会创建多个Data对象,为了保证每个Data对象的expando属性的值不相等,所以这么搞 this.expando = jQuery.expando + Math.random(); } Data.uid = 1; // Data函数的属性,'静态属性' Data.accepts = jQuery.acceptData; Data.prototype = { key: function( owner ) { // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return the key for a frozen object. // 若owner在该缓存池中存在对应的缓存对象,则返回混存对象的key(是一个数字), // 若owner在该缓存池中不存在对应的缓存对象,则在缓存池中为其创建一个缓存对象,并返回该缓存对象的key if ( !Data.accepts( owner ) ) { return 0; } var descriptor = {}, // Check if the owner object already has a cache key // 检查owner对象在该缓存池中是否存在缓存 unlock = owner[ this.expando ]; // 是一个数字,用来作为缓存池中缓存对象的key // If not, create one // 如果没有,则创建一个 if ( !unlock ) { unlock = Data.uid++; // Secure it in a non-enumerable, non-writable property // 给owner附加一个属性 owner[this.expando] = unlock ,并且该属性不能被枚举, try { descriptor[ this.expando ] = { value: unlock }; Object.defineProperties( owner, descriptor ); // Support: Android<4>

可能会有同学问道:如果我想对dataPriv进行操作该如何?

请看源码:

 jQuery.extend({ hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } });

通过源码,我们可以看出:

jQuery.data() jQuery.remove()都是对dataUser进行操作,而jQuery._data() jQuery._remove()都是对dataPriv进行操作。

理解jQuery.data(ele,name,data) 与 jQuery().data(key,value)的不同。

  通过上面的源码,我们可以看到jQuery.data(ele,name,data)是对ele元素附加数据。

  而jQuery().data(key,value)则会为jQuery对象中的所有DOM对象分别附加数据

来看源码(删减了部分):

 jQuery.fn.extend({ data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes;return access( this, function( value ) { var data, camelKey = jQuery.camelCase( key ); // 从这里可以看出,为jQuery对象中的每个DOM元素分别附加数据 this.each(function() { // First, attempt to store a copy or reference of any // data that might've been store with a camelCased key. var data = dataUser.get( this, camelKey ); // For HTML5 data-* attribute interop, we have to // store property names with dashes in a camelCase form. // This might not apply to all properties...* dataUser.set( this, camelKey, value ); // *... In the case of properties that might _actually_ // have dashes, we need to also store a copy of that // unchanged property. if ( key.indexOf("-") !== -1 && data !== undefined ) { dataUser.set( this, key, value ); } }); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each(function() { dataUser.remove( this, key ); }); } });

上文中的所有源码:为jQuery.1.12

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者动作能带来一定的帮助,如果有疑问大家可以留言交流。

以上就是深入学习jQuery中的data()的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » JavaScript 教程