先前在分析Sizzle的时候分析到Sizzle有自己的缓存机制点击这里查看。不过Sizzle的缓存只是对内使用的(内部自己存,自己取)。接下来分析jQuery可以对外使用的缓存(可存可取)。

  首先需要明白jQuery缓存需要解决什么问题,实现它的意义?

  jQuery缓存要解决的是在往DOM节点添加数据(这些数据往往和该DOM节点紧密相关),但是给DOM添加数据或自定义属性可能起内存泄漏(DOM发生缓存泄漏导致DOM的数据没法被删除,那么添加到DOM的数据也无法被回收。久而久之,会出现一大片内存得不到释放。),所以应该要尽量避免这样做。更好的解决方法是使用一种低耦合的方式让DOM和缓存数据能够联系起来。

  jQuery怎么做?

  jQuery定义了一个属性cache = {}来保存所有的缓存数据。在DOM节点上添加一个expando的值(expando的值等于”jQuery”+当前时间)为属性名称的属性,这个属性的值id = dom[jQuery.expando]用来查找jQuery.cache上对应的缓存数据:jQuery.cache[id].data 即为DOM对应的缓存数据。为了保证了id 的全局唯一性,这个id使用jQuery.guid自增。例如

$("#demo").data("name","chua");
$("#demo").data("name");//"chua"

  jQuery底层接口还做了拓展,不仅仅能缓存在DOM节点的数据,还可以缓存非DOM节点的对象的数据。这种方式最终数据是附加到了对象obj自己身上,而没有使用全局缓存jQuery.cache。该方式也会在对象obj上添加一个expando的值为属性名称的属性,缓存数据保存在obj[jQuery.expando].data上。不过这个一般不推荐外部使用,因为调用的是更底层的api: jQuery.data,而非$(...).data。例子:

var obj = {};
$.data(obj,'name','chua');
$.data(obj,'name');//"chua"

  

  接下来我们开始解析源码。

jQuery.fn.extend({
    data: function( key, value ) {…    },//内部使用基础api:jQuery.data来实现,可以使用参数(key,value),也可以使用参数(obj)
    removeData: function( key ) {…}//内部使用基础api: jQuery.removeData来实现
});

所以我们主要看底层基础API部分

jQuery.extend({
    cache: {},
    // 每个jQuery拷贝都有一个其唯一的标志。比如你的页面有两个iframe且每个iframe都用到的jQuery。name你的两个iframe就有两份jQuery拷贝。
    expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
    // 下面的元素将抛出不可捕获的异常,如果你尝试给他们添加expando属性
    //主要用在acceptData函数中确定元素是否可以添加expando属性
    noData: {
        "embed": true,
        // Ban all objects except for Flash (which handle expandos)
        "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
        "applet": true
    },
    hasData: function( elem ) {…},
  data: function( elem, name, data ) {return internalData( elem, name, data );},
  removeData: function( elem, name ) {return internalRemoveData( elem, name );},
    // 内部使用
    _data: function( elem, name, data ) {return internalData( elem, name, data, true );},
    _removeData: function( elem, name ) {return internalRemoveData( elem, name, true );},
    // 用来确定DOM节点是否能够添加expando 数据
    acceptData: function( elem ) {
        // non-element不能添加数据
        if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
            return false;
        }
        var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
        // nodes accept data unless otherwise specified; rejection can be conditional
        return !noData || noData !== true && elem.getAttribute("classid") === noData;
    }
});

  所以归根结底缓存数据和取出缓存数据是使用内部函数internalData( elem, name, data, pvt /* Internal Use Only */ )

  

a. 存取缓存的内部函数function internalData( elem, name, data, pvt /* Internal Use Only */ )


   internalData的处理流程为:

  1.判断如果元素不支持添加属性的直接返回。取出缓存容器和缓存中使用的ID备用。这里面粉两种情况,如果传递的对象elem是DOM对象,则缓存取全局缓存容器jQuery.cache,id取elem[jQuery.expando](如果没有的话使用jQuery.guid自增值设置一个);如果elem不是DOM对象,则缓存容器取对象本身elem,id取jQuery.expando

jQuery-1.9.1源码分析系列(四) 缓存系统
var thisCache, ret,
        //获取每份jquery拷贝的标志
        internalKey = jQuery.expando,
        getByName = typeof name === "string",

        //我们将DOM节点和JS对象区分开来,因为IE6-7不能跨越DOM-JS界限正确回收对象引用
        //所有DOM节点上没有附加数据,而是存放再来全局jQuery缓存jQuery.cache中
        isNode = elem.nodeType,

        //只有DOM节点需要全局jQuery缓存;js对象数据直接附加到对象上使得垃圾回收机制能够自动回收
        cache = isNode ? jQuery.cache : elem,

        //假如JS对象的缓存已经存在,则拷贝标志作为一个访问ID。
        //如果是DOM对象,则拷贝标志作为一个属性附加到dom上,这个属性的值作为访问全局缓存的一个路径ID
        id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;

    //预先处理要获取数据,但是缓存中没有数据的情况。这种情况应当直接返回
    if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
        return;
    }

    if ( !id ) {
        //只有dom节点的每个元素都需要唯一的ID,直到他们的数据在全局缓存中清除
        if ( isNode ) {
            //启用删除的id中最后一个id,或是对象的全局GUID统计
            elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
        } else {
            id = internalKey;
        }
    }
View Code

相关文章:

  • 2021-11-29
  • 2021-10-03
  • 2021-10-01
  • 2021-12-27
  • 2021-08-12
  • 2021-11-15
  • 2021-10-12
  • 2022-02-11
猜你喜欢
  • 2021-12-19
  • 2021-07-31
  • 2021-12-06
  • 2021-10-11
  • 2021-06-05
  • 2021-12-16
  • 2021-06-29
相关资源
相似解决方案