zoukankan      html  css  js  c++  java
  • jQuery1.9.1data缓存对象源码分析

      1 // 匹配结尾是否有“{...}”或"[...]"
      2     var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
      3         // 匹配大写字母
      4         rmultiDash = /([A-Z])/g;
      5 
      6     /**
      7      * 内部用来设置/获取元素或对象的缓存方法
      8      *
      9      * @param elem DOM元素或者JS对象
     10      * @param name 缓存的标识符key
     11      * @param data 缓存数据
     12      * @param {Boolean} pvt 当为true时表示是私有性的,jq内部使用的
     13      */
     14 
     15     function internalData(elem, name, data, pvt /* Internal Use Only */ ) {
     16         // 判断该对象能不能绑定数据
     17         if (!jQuery.acceptData(elem)) {
     18             return;
     19         }
     20 
     21         var thisCache, ret,
     22             // expando是jQuery生成的随机ID
     23             internalKey = jQuery.expando,
     24             getByName = typeof name === 'string',
     25             // 我们不得不分别处理DOM元素和js对象,
     26             // 因为ie6/7的垃圾回收不能正确处理对DOM元素的对象引用
     27             isNode = elem.nodeType,
     28             // 只有DOM元素才需要全局jQuery.cache对象。
     29             // js对象数据直接指向该对象,垃圾回收可以自动处理
     30             cache = isNode ? jQuery.cache : elem,
     31             // 1. 如果是dom元素,返回dom元素通过expando对应的id(值可能为undefined)
     32             // 2. 如果是普通js对象,分两种情况:
     33             //    2.1 如果js对象存在通过expando对应的值,即代表有缓存数据,则立即返回expando作为id
     34             //    2.2 如果没有对应值,则代表没有缓存数据,此时返回undefined
     35             // 也就是说如果id不为空,那么肯定是有存储数据过的
     36             id = isNode ? elem[internalKey] : elem[internalKey] && internalKey;
     37 
     38         // 当一个对象没有data的时候返回,避免多余工作
     39         if ((!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined) {
     40             return;
     41         }
     42 
     43         // 如果没有ID
     44         if (!id) {
     45             // 如果是DOM元素,给该节点绑定一个属性ID
     46             if (isNode) {
     47                 elem[internalKey] = id = core_deletedIds.pop() || jQuery.guid++;
     48             } else {
     49                 // 否则是对象则通过expando创建一个唯一ID
     50                 id = internalKey;
     51             }
     52         }
     53 
     54         // 如果cache对象没有指定id属性
     55         if (!cache[id]) {
     56             cache[id] = {};
     57 
     58             // 当为JS对象时,为了避免被JSON.stringify序列化
     59             // 这里将toJSON方法设为空方法,这样就会返回空值
     60             if (!isNode) {
     61                 cache[id].toJSON = jQuery.noop;
     62             }
     63         }
     64 
     65 
     66         // 如果name是对象或函数,当存在pvt将name浅复制给cache[id],
     67         // 否则浅复制给cache[id].data
     68         if (typeof name === 'object' || typeof name === 'function') {
     69             if (pvt) {
     70                 cache[id] = jQuery.extend(cache[id], name);
     71             } else {
     72                 cache[id].data = jQuery.extend(cache[id].data, name);
     73             }
     74         }
     75 
     76         thisCache = cache[id];
     77 
     78         // 为了防止系统内部数据和用户自定义数据的key发生冲突,才将用户数据包在thisCache.data中,
     79         // pvt的意思是保持私有性,非私有性时对外提供data属性对象
     80         // 系统内部数据就是thisCache中
     81         if (!pvt) {
     82             if (!thisCache.data) {
     83                 thisCache.data = {};
     84             }
     85 
     86             // 只对外开放thisCache.data属性值
     87             thisCache = thisCache.data;
     88         }
     89 
     90         // 如果data不为undefined,将data赋值给thisCache的通过驼峰式的name属性
     91         if (data !== undefined) {
     92             thisCache[jQuery.camelCase(name)] = data;
     93         }
     94 
     95         // 如果name是字符串
     96         if (getByName) {
     97             // 尝试获取thisCache的属性data
     98             ret = thisCache[name];
     99 
    100             // 如果ret为null或undefined,则尝试获取驼峰式的name属性data值
    101             if (ret == null) {
    102                 ret = thisCache[jQuery.camelCase(name)];
    103             }
    104         } else {
    105             // 否则name为非字符串时,ret指向thisCache
    106             ret = thisCache;
    107         }
    108 
    109         return ret;
    110     }
    111 
    112     /**
    113      * 删除对应的缓存数据
    114      *
    115      * @param elem
    116      * @param name
    117      * @param pvt
    118      */
    119 
    120     function internalRemoveData(elem, name, pvt) {
    121         if (!jQuery.acceptData(elem)) {
    122             return;
    123         }
    124 
    125         var i, l, thisCache,
    126             isNode = elem.nodeType,
    127             cache = isNode ? jQuery.cache : elem,
    128             id = isNode ? elem[jQuery.expando] : jQuery.expando;
    129 
    130         // 如果没有缓存对象,返回
    131         if (!cache[id]) {
    132             return;
    133         }
    134 
    135         if (name) {
    136             thisCache = pvt ? cache[id] : cache[id].data;
    137 
    138             if (thisCache) {
    139                 // 支持单个的key
    140                 // 数组,多个key,如:[key1, key2, key3, ...]
    141                 // 字符串,多个key,用空格隔开,如:'key1 key2 key3 ...'
    142 
    143                 // 如果name不是数组类型,将name转换为数组类型
    144                 if (!jQuery.isArray(name)) {
    145                     // 如果name是thisCache的一个属性key
    146                     if (name in thisCache) {
    147                         // 用数组保存
    148                         name = [name];
    149                     } else {
    150                         // 将name驼峰化
    151                         name = jQuery.camelCase(name);
    152                         // 此时若name是thisCache的一个属性key
    153                         if (name in thisCache) {
    154                             // 同样转换成数组
    155                             name = [name];
    156                         } else {
    157                             // 否则name是个多个空白分隔的字符串
    158                             name = name.split(' ');
    159                         }
    160                     }
    161                     // 如果是数组,将name数组各项驼峰化后追加到name数组里
    162                 } else {
    163                     name = name.concat(jQuery.map(name, jQuery.camelCase));
    164                 }
    165 
    166                 // 遍历删除name数组里的各项key属性
    167                 for (i = 0, l = name.length; i < l; i++) {
    168                     delete thisCache[name[i]];
    169                 }
    170 
    171                 // 如果pvt为true,检查thisCache是否为空的数据对象,如果不是直接退出函数
    172                 // 如果pvt为false,判断thisCache是否为空对象,如果不是也是退出
    173                 // 这里考虑到用户自定义或者其他私有受保护的属性
    174                 if (!(pvt ? isEmptyDataObject : jQuery.isEmptyObject)(thisCache)) {
    175                     return;
    176                 }
    177             }
    178         }
    179 
    180         // 如果pvt为false,即非私有性
    181         // 删除data属性值
    182         if (!pvt) {
    183             delete cache[id].data;
    184 
    185             // 同理,这时cache[id]还存在其他属性,退出
    186             if (!isEmptyDataObject(cache[id])) {
    187                 return;
    188             }
    189         }
    190 
    191         // 如果是DOM元素,清除绑定在elem上的所有数据
    192         if (isNode) {
    193             jQuery.cleanData([elem], true);
    194         } else if (jQuery.support.deleteExpando || cache != cache.window) {
    195             // 如果支持删除绑定在对象上的expando属性或者cache非window对象
    196             // 只用delete就可以删除了
    197             delete cache[id];
    198         } else {
    199             // 其他情况就将属性设为null来清空缓存
    200             cache[id] = null;
    201         }
    202     }
    203 
    204     jQuery.extend({
    205         // 当是DOM元素的时候,使用$.cache来缓存数据
    206         cache: {},
    207         // 生成expando字符串
    208         expando: 'jQuery' + (core_version + Math.random()).replace(/\D/g, ''),
    209         // 以下情况不需要缓存
    210         noData: {
    211             'embed': true,
    212             'object': 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000',
    213             'applet': true
    214         },
    215         // 判断是否已有缓存数据
    216         hasData: function(elem) {
    217             elem = elem.nodeType ? jQuery.cache[elem[jQuery.expando]] : elem[jQuery.expando];
    218             return !!elem && !isEmptyDataObject(elem);
    219         },
    220         // 适配器模式
    221         data: function(elem, name, data) {
    222             return internalData(elem, name, data);
    223         },
    224         removeData: function(elem, name) {
    225             return internalRemoveData(elem, name);
    226         },
    227         // 私有方法
    228         _data: function(elem, name, data) {
    229             return internalData(elem, name, data, true);
    230         },
    231         _removeData: function(elem, name) {
    232             return internalRemoveData(elem, name, true);
    233         },
    234         // 判断元素或对象是否可以缓存
    235         acceptData: function(elem) {
    236             if (elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9) {
    237                 return false;
    238             }
    239 
    240             var noData = elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()];
    241 
    242             return !noData || noData !== true && elem.getAttribute('classid') === noData;
    243         }
    244     });
    245 
    246     jQuery.fn.extend({
    247         data: function(key, value) {
    248             var attrs, name,
    249                 elem = this[0],
    250                 i = 0,
    251                 data = null;
    252 
    253             // 如果key为undefined,说明key和value都为空,获取缓存data
    254             if (key === undefined) {
    255                 // 如果有DOM元素
    256                 if (this.length) {
    257                     // 获取以前保存在elem的data
    258                     data = jQuery.data(elem);
    259 
    260                     // 对于元素节点而言,数据可以来自两个地方:
    261                     // 1. jQuery.cache缓存中,之前手动存进去的,如:$dom.data('data1', value1);
    262                     // 2. 来自html标签的以data-开头的属性,之后该属性的数据也会被存储到jQuery.cache缓存中
    263 
    264                     // 如果元素节点的jQuery.cache['parsedAttrs']的值为null | false | undefined
    265                     // 说明elem的属性节点没有被解析过,下面就进行解析
    266                     if (elem.nodeType === 1 && !jQuery._data(elem, 'parsedAttrs')) {
    267                         // 获得elem的属性列表
    268                         attrs = elem.attributes;
    269                         for (; i < attrs.length; i++) {
    270                             // 该属性名称
    271                             name = attrs[i].name;
    272 
    273                             // 如果name有"data-"字符
    274                             if (!name.indexOf('data-')) {
    275                                 // 将name驼峰化:"dataCustom"
    276                                 name = jQuery.camelCase(name.slice(5));
    277 
    278                                 // 如果没有对应的缓存,就将html5的“data-”值(转换后)设置为相应的缓存值
    279                                 dataAttr(elem, name, data[name]);
    280                             }
    281                         }
    282                         // 给缓存对象添加私有缓存,并把缓存值设置为true
    283                         // 用来标记已经解析过属性
    284                         jQuery._data(elem, 'parseAttrs', true);
    285                     }
    286                 }
    287 
    288                 return data;
    289             }
    290 
    291             // 如果key是对象,直接将其拷贝到jQuery.cache.data缓存对象里
    292             // 用来设置多个值的情况
    293             if (typeof key === 'object') {
    294                 return this.each(function() {
    295                     jQuery.data(this, key);
    296                 });
    297             }
    298 
    299             // 为每个元素执行函数后返回原始的元素集(this)
    300             return jQuery.access(this, function(value) {
    301                 if (value === undefined) {
    302                     // 如果value未定义并且在jQuery.cache缓存中没有找到相应key的缓存,
    303                     // 然后再试图查看HTML5标签的“data-”属性是否被解析过了
    304                     return elem ? dataAttr(elem, key, jQuery.data(elem, key)) : null;
    305                 }
    306             }, null, value, arguments.length > 1, null, true);
    307         },
    308         removeData: function(key) {
    309             return this.each(function() {
    310                 jQuery.removeData(this, key);
    311             });
    312         }
    313     });
    314 
    315     // 处理元素节点中使用HTML5的“data-test”属性,并将其转换到相应的类型存储到jQuery.cache对象中
    316 
    317     function dataAttr(elem, key, data) {
    318         // 如果data为空且elem是元素节点,那么将HTML5的data-属性值转换为相应的类型
    319         if (data === undefined && elem.nodeType === 1) {
    320             // 反驼峰化
    321             var name = 'data-' + key.replace(rmultiDash, '-$1').toLowerCase();
    322 
    323             // 获取data字符串属性值
    324             data = elem.getAttribute(name);
    325 
    326             if (typeof data === 'string') {
    327                 try {
    328                     // 布尔型
    329                     data = data === 'true' ? true :
    330                         data === 'false' ? false :
    331                     // null
    332                     data === 'null' ? null :
    333                     // +data只会将数字字符转换成数字,再加上""则会转换回字符串
    334                     // 这里是测试是否为数字
    335                     +data + '' === data ? +data :
    336                     // 数组或对象,并转换
    337                     rbrace.test(data) ? jQuery.parseJSON(data) :
    338                     // 其他类型
    339                     data;
    340                 } catch (e) {}
    341 
    342                 // 将格式化的数据存在jQuery.cache缓存。
    343                 jQuery.data(elem, key, data);
    344             } else {
    345                 // 如果该属性不存在,此时data为null,将其转换为undefined
    346                 data = undefined;
    347             }
    348         }
    349 
    350         // 返回data-属性值(转换后)的类型
    351         return data;
    352     }
    353 
    354     // 检查缓存对象的数据是否为空
    355 
    356     function isEmptyDataObject(obj) {
    357         var name;
    358         for (name in obj) {
    359             // 如果公共data为空,那么私有对象也为空
    360             if (name === 'data' && jQuery.isEmptyObject(obj[name])) {
    361                 continue;
    362             }
    363             if (name !== 'toJSON') {
    364                 return false;
    365             }
    366         }
    367 
    368         return true;
    369     }
  • 相关阅读:
    中台之交付
    mysql之事务
    中台之中台的设计
    0318 guava并发工具
    0312 java接口测试三棱军刺rest-assured
    0309 软件基本原理1
    0308 软件系统的非功能需求
    PELT(Per-Entity Load Tracking)
    CPU亲和度
    硬件相关知识随手笔记
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3106894.html
Copyright © 2011-2022 走看看