jQuery缓存技术的简单探究

如果与java语言世界对比的话,jQuery Data模块和jQuery Queue模块类似java的集合框架。jQuery Data模块的功能简单来说就是"在某个元素"上对缓存数据"做增删改查的CRUD操作"。"某个元素"可能是原生的dom对象,也可能是js内核对象,jQuery Data模块区别对待它们。按照jQuery通用编程风格,一般对数据的写和读是设计到同一个方法的,更新也属于写操作类型,这样就只有两个方法了--data()和removeData(),当然一般还会设计一个判断数据是否存在的hasData()方法。另外,jQuery Data模块考虑到了私有数据存储的需求并从逻辑(而不是语法)上实现了一个方案_data(),对于某些特殊的不支持数据存储的dom元素,jQuery Data给出了判断方法acceptData()。

一 jQuery Data模块的API

先看jQuery Data模块的几种常见使用方法。

1 写数据
(1) 写单条数据
jQuery.prototype实例方法: $("x").data("p1", "v1");
jQuery静态方法:jQuery.data(elem, "p1", "v1");
(2) 写多条数据
jQuery.prototype实例方法: $("x").data({"p1":"v1","p2":"v2","p3":"v3"});
jQuery静态方法:jQuery.data(elem, {"p1":"v1","p2":"v2","p3":"v3"});

2 读数据
(1) 读单条数据
jQuery.prototype实例方法: $("x").data("p1");
jQuery静态方法:jQuery.data(elem, "p1");
(2) 读全部数据
jQuery.prototype实例方法: $("x").data();
jQuery静态方法:jQuery.data(elem);

3 删数据
jQuery.prototype实例方法: $("x").removeData("p1");
jQuery静态方法:jQuery.removeData(elem, "p1");

二 设计方案

jQuery Data模块设计思路相对其他模块而言是比较简单的,这里概略总结一下。
1 对于原生dom对象
首先排除那些不能接受data缓存的dom元素类型,这些类型被标记于noData对象中,只要dom对象节点名称和对应属性符合noData集合规则的就是不能接受data缓存的dom类型:'var match = jQuery.noData[ elem.nodeName.toLowerCase() ];',对应acceptData()方法将返回false。
对于那些能接受data缓存的dom对象,每个对象的数据缓存在全局缓存对象jQuery.cache中,而不是dom对象本身中!这一点是关键点,在全局缓存对象jQuery.cache中为每一个dom对象开辟一个缓存对象缓存该dom的data数据,由此引出一个问题是:如何关联上不同的dom对象到全局缓存对象jQuery.cache的不同缓存对象上?jQuery的方案是给每一个dom对象添加一个属性,该属性名称为'jQuery.expando'变量的值,这个值由三个部分组成:'jQuery'、'version'、'random数字值',其规则是'expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),',这个变量的值这样设计是为了最大化保证不与用户自定义变量值雷同而引起冲突,所有能缓存data的dom对象的该属性名称都是一致的;但是每个dom对象该属性的值却是唯一的,类似于关系型数据库表的主键规则,由一个全局uuid唯一生成'elem[ jQuery.expando ] = id = ++jQuery.uuid;',这个值是唯一的且递增的整数值。
dom对象属性'jQuery.expando'的值'jQuery.uuid'将作为全局缓存对象的key,全局缓存对象的value就是独立的一个缓存对象thisCache,存储对应dom对象需要缓存的所有data。这样根据每个不同的dom对象的属性值就能在全局缓存对象jQuery.cache中唯一对应上一个独立缓存对象,对dom对象缓存data的CRUD操作说白了就是对这个独立缓存对象thisCache的CRUD操作。

2 对于js内核对象
js内核对象的缓存就没那么绕了,因为js内核对象本身就可以开辟一个属性作为独立缓存对象thisCache,这个属性的名称也简单,就是'jQuery.expando'的值,所有js内核对象的缓存属性的名称都是一致的。至于dom对象为什么不能向js内核对象一样直接存储缓存数据,源码中给出的解释原因是涉及到浏览器的内存回收机制--如果直接在dom对象上添加独立缓存对象,那么这个dom对象由于引用了独立缓存对象,垃圾回收机制根据'引用计数法'将无法判定该dom对象是可以被回收的,这样长时间运行积累下来可能导致OOM(这个理解可能不准确)。

三 主要方法的源码分析

1 jQuery.data( elem, name, data, pvt /* Internal Use Only */ )方法源码
由于data一个方法就承担数据操作的C/R/U三种类型,所以其参数调用和内部实现逻辑比较多样化。

 data: function( elem, name, data, pvt /* Internal Use Only */ ) {
  /** 对于不能读写的dom类型对象直接返回。*/
  if ( !jQuery.acceptData( elem ) ) {
   return;
  }
  /**
  确定该方法是读还是写--name参数和data参数都是undefined的话是读全部,name参数为string类型且data为undefined的话是读单条,其他调用是写。
  确定该方法是对dom对象操作还是js对象操作--直接看elem参数有没有nodeType属性。
  确定当前elem对象的id--是dom对象的话就是elem[ jQuery.expando ],否则直接为jQuery.expando。
  确定父级缓存对象cache--是dom对象的话就是全局缓存对象jQuery.cache,否则就是elem本身。*/
  var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
   // We have to handle DOM nodes and JS objects differently because IE6-7
   // can't GC object references properly across the DOM-JS boundary
   isNode = elem.nodeType,
   // Only DOM nodes need the global jQuery cache; JS object data is
   // attached directly to the object so GC can occur automatically
   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[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
  /** 如果是读方法但是id为空的话(说明还未存储过data)直接返回。 */
  // Avoid doing any more work than we need to when trying to get data on an
  // object that has no data at all
  if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
   return;
  }
  /** 如果id不存在为其构造一个--是dom对象的话由全局uuid生成唯一值,否则直接为jQuery.expando。 */
  if ( !id ) {
   // Only DOM nodes need a new unique ID for each element since their data
   // ends up in the global cache
   if ( isNode ) {
    elem[ jQuery.expando ] = id = ++jQuery.uuid;
   } else {
    id = jQuery.expando;
   }
  }
  /** 如果子级缓存对象cache[ id ]不存在为其构造一个空的对象--非dom的js内核对象需要为其子级缓存对象引入一个toJSON属性。 */
  if ( !cache[ id ] ) {
   cache[ id ] = {};
   // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
   // metadata on plain JS objects when the object is serialized using
   // JSON.stringify
   if ( !isNode ) {
    cache[ id ].toJSON = jQuery.noop;
   }
  }
  /** 如果是写多条数据的调用,则把需要写入的数据扩展到子级缓存对象thisCache中去。 */
  // An object can be passed to jQuery.data instead of a key/value pair; this gets
  // shallow copied over onto the existing cache
  if ( typeof name === "object" || typeof name === "function" ) {
   if ( pvt ) {
    cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
   } else {
    cache[ id ] = jQuery.extend(cache[ id ], name);
   }
  }
  /** 判定子级缓存对象thisCache--是dom对象的话为jQuery.cache[ id ],否则就是elem[ id ]。 */
  thisCache = cache[ id ];
  // Internal jQuery data is stored in a separate object inside the object's data
  // cache in order to avoid key collisions between internal data and user-defined
  // data
  if ( pvt ) {
   if ( !thisCache[ internalKey ] ) {
    thisCache[ internalKey ] = {};
   }
   thisCache = thisCache[ internalKey ];
  }
  /** 如果是写单条数据的调用,则写入一条数据到子级缓存对象thisCache上。 */
  if ( data !== undefined ) {
   thisCache[ jQuery.camelCase( name ) ] = data;
  }
  // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
  // not attempt to inspect the internal events object using jQuery.data, as this
  // internal data object is undocumented and subject to change.
  if ( name === "events" && !thisCache[name] ) {
   return thisCache[ internalKey ] && thisCache[ internalKey ].events;
  }
  /** 如果是读单条/多条数据的调用,则返回一条数据/全部数据。 */
  return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache;
 },
2 jQuery.removeData( elem, name, pvt /* Internal Use Only */ )方法源码

removeData方法只承担数据操作的D类型,相对功能单一,方法内部流程类似,不同的是当子级独立缓存对象内部已经没有缓存的数据了时需要delete掉该独立缓存对象本身以及dom对象的对应属性。

 removeData: function( elem, name, pvt /* Internal Use Only */ ) {
  if ( !jQuery.acceptData( elem ) ) {
   return;
  }
  var internalKey = jQuery.expando, isNode = elem.nodeType,
   // See jQuery.data for more information
   cache = isNode ? jQuery.cache : elem,
   // See jQuery.data for more information
   id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
  // If there is already no cache entry for this object, there is no
  // purpose in continuing
  if ( !cache[ id ] ) {
   return;
  }
  if ( name ) {
   var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
   if ( thisCache ) {
    delete thisCache[ name ];
    // If there is no data left in the cache, we want to continue
    // and let the cache object itself get destroyed
    if ( !isEmptyDataObject(thisCache) ) {
     return;
    }
   }
  }
  // See jQuery.data for more information
  if ( pvt ) {
   delete cache[ id ][ internalKey ];
   // Don't destroy the parent cache unless the internal data object
   // had been the only thing left in it
   if ( !isEmptyDataObject(cache[ id ]) ) {
    return;
   }
  }
  var internalCache = cache[ id ][ internalKey ];
  // Browsers that fail expando deletion also refuse to delete expandos on
  // the window, but it will allow it on all other JS objects; other browsers
  // don't care
  if ( jQuery.support.deleteExpando || cache != window ) {
   delete cache[ id ];
  } else {
   cache[ id ] = null;
  }
  // We destroyed the entire user cache at once because it's faster than
  // iterating through each key, but we need to continue to persist internal
  // data if it existed
  if ( internalCache ) {
   cache[ id ] = {};
   // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
   // metadata on plain JS objects when the object is serialized using
   // JSON.stringify
   if ( !isNode ) {
    cache[ id ].toJSON = jQuery.noop;
   }
   cache[ id ][ internalKey ] = internalCache;
  // Otherwise, we need to eliminate the expando on the node to avoid
  // false lookups in the cache for entries that no longer exist
  } else if ( isNode ) {
   // IE does not allow us to delete expando properties from nodes,
   // nor does it have a removeAttribute function on Document nodes;
   // we must handle all of these cases
   if ( jQuery.support.deleteExpando ) {
    delete elem[ jQuery.expando ];
   } else if ( elem.removeAttribute ) {
    elem.removeAttribute( jQuery.expando );
   } else {
    elem[ jQuery.expando ] = null;
   }
  }
 },

四 内部私有数据/二级缓存方案

如果调用方希望缓存的data不是外部可访问的,jQuery Data对于这些数据的存储地点是单独维护的,位于子级独立缓存对象的内部名称同样为'jQuery.expando'的属性值对象中,相当于是一个二级缓存方案,jQuery Data模块的调用者知道怎么访问和存入数据,其他用户则不关心这块数据,这是_data()方法通过调用'jQuery.data( elem, name, data, true );'方法并传入第四个参数pvt为true来实现的,jQuery.data()方法内部有独立的逻辑判断并处理私有数据。

jQuery Data模块的实现是比较通用的,即它支持在dom对象/内核对象上存储"任意类型"的缓存数据,这些缓存数据将用于上层模块,按照上层模块的意图和需求,这些缓存数据可以是任意类型的。比如对jQuery Event模块的需求而言,在选择集dom元素上存入的缓存数据是"可执行函数集合",这个时候jQuery Data模块中缓存的数据对象的作用就非常类似jQuery Deffered模块中的回调函数存储对象了,也即它扮演的是"弹夹"的角色,按照每个dom元素事件类型分别存储一个事件回调函数队列,就像不同口径不同规格的子弹被各自的弹夹存放一样。对于其他模块而言,不一定只存储函数数据,完全可以根据模块设计者的需求存储其他类型数据。后续分析jQuery Event模块时将深入该主题。

时间: 2025-01-20 15:06:34

jQuery缓存技术的简单探究的相关文章

使用jQuery在对象中缓存选择器的简单方法

  这篇文章主要介绍了使用jQuery在对象中缓存选择器的简单方法,jQuery是最知名的JavaScript库,需要的朋友可以参考下 当使用像jQuery这样的库时,开发者通常会使用选择器来访问和操作DOM中的元素.当一个选择在页面上被反复的访问时,把它缓存起来以获得更好的性能是个不错的想法. 让我们看一个例子, ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 jQuery(document).ready(function() { jQuery('

深入探讨PHP缓存技术

PHP,一门最近几年兴起的Web设计脚本语言,由于它的强大和可伸缩性,近几年来得到长足的发展,PHP相比传统的ASP网站,在速度上有绝对的优势,想mssql转6万条数据PHP如需要40秒,ASP不下2分钟.但是,由于网站的数据越来越多,我们渴求能更快速的调用数据,不必要每次都从数据库掉,我们可以从其他的地方,比方一个文件,或者某个内存地址,这就是PHP的缓存技术,也就是Cache技术. 分析深入 一般来说,缓存的目的是把数据放在一个地方让访问的更快点,毫无疑问,内存是最快的,但是,几百M的数据能

缓存技术及在Rainbow Portal的应用

缓存 1. ASP.NET缓存技术概述 将数据库中的数据缓存到内存(也可以存储在其他场所),则无需在请求每个页面时都访问数据库.由于从内存中返回数据的速度始终比新提供的数据速度快,因而可以大大提高应用程序的性能. ASP.NET为你使用缓存技术提供最大的灵活性,你可以缓存整个HTML页面,或是部分HTML页面,或是各种对象.你可以设置过期策略,或是设置依赖性,即在其他资源如文件或数据库表改变时,自动移出缓存. ASP.NET中有两种基本的缓存: 输出缓存 页面输出缓存是最为简单的缓存机制,该机制

缓存技术详谈和代码实现

缓存 ccdot /2006-2-26 3:50:32 /324/ /*Author: 老农 Last modify:2006-2-26Author URL:http://www.cjjer.comDes cription:本篇关于缓存,出蓝色理想他站谢绝转载,否则QXDD.,在php5下通过.*/   一: 引论 PHP,一门最近几年兴起的web设计脚本语言,由于它的强大和可伸缩性,近几年来得到长足的发展,php相比传统的asp网站,在速度上有绝对的优势,想mssql转6万条数据php如需要4

ASP.Net使用缓存技术提高效率

asp.net|缓存     ASP.Net的缓存技术大大地提高了效率,本人将代码的简单的实现代码贴出: 一.在页面中显示时,读缓存数据,加载XML数据 public void LoadData()   {   DataView Source = (DataView)Cache["MyData"];   if(Source == null)    {    DataSet ds = new DataSet();    FileStream fs = new FileStream(Ser

PHP缓存技术

1普遍缓存技术 数据缓存:这里所说的数据缓存是指数据库查询缓存,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据,并把查询结果序列化后保存到文件中,以后同样的查询结果就直接从缓存表或文件中获得. 用的最广的例子看Discuz的搜索功能,把结果ID缓存到一个表中,下次搜索相同关键字时先搜索缓存表. 举个常用的方法,多表关联的时候,把附表中的内容生成数组保存到主表的一个字段中,需要的时候数组分解一下,这样的好处是只读一个表,坏处就是两个数据同步会多不少步骤,数

android 自定义view 缓存技术

在android应用开发过程中,常常涉及到需要自己来重写一个view.一般情况下,只需要重写view中的onDraw方法就能够实现绘制的工作,但是有的时候,涉及到多层且动态的效果的话,不加处理而直接在onDraw中直接绘制的开销会十分大,这种情况下,应用缓存技术就十分有必要了. 缓存技术的原理其实十分简单,就是事先将需要展现的界面全部绘制完成,然后在onDraw里面直接将缓存好的界面展示出来就可以了, 不需要涉及太多的开销. 在这里,可以采用bitmap做为缓存的介质,需要事先创建对应大小的bi

php缓存技术介绍_php技巧

缓存是指临时文件交换区,电脑把最常用的文件从存储器里提出来临时放在缓存里,就像把工具和材料搬上工作台一样,这样会比用时现去仓库取更方便.因为缓存往往使用的是RAM(断电即掉的非永久储存),所以在忙完后还是会把文件送到硬盘等存储器里永久存储.电脑里最大的缓存就是内存条了,最快的是CPU上镶的L1和L2缓存,显卡的显存是给GPU用的缓存,硬盘上也有16M或者32M的缓存.千万不能把缓存理解成一个东西,它是一种处理方式的统称! 在WEB开发中用来应付高流量最有效的办法就是用缓存技术,能有效的提高服务器

JAVA缓存技术之EhCache(转)

最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/articles/cache-static-files-with-jnotify-and-ehcache 介绍 JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacO