jQuery之解读jQuery.data

jQuery中很多其他组件都用到data功能,源码与注解如下


(function (jQuery) {

    var rbrace = /^(?:\{.*\}|\[.*\])$/, //非获取匹配,数组或者json对象
        rmultiDash = /([A-Z])/g;

    jQuery.extend({
        cache: {},
        uuid: 0,
        expando: "jQuery" + (jQuery.fn.jQuery + Math.random()).replace(/\D/g, ""),
        noData: {
            "embed": true,
            "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
            "applet":true
        },
        hasData: function (elem) {
            elem = elem.nodeType ? jQuery.cache[elem[jQuery.expando]] : elem[jQuery.expando];
            return !!elem && !isEmptyDataObject(elem);
        },
        data: function (elem, name, data, pvt/*仅jQuery内部使用*/) {
            if (!jQuery.acceptData(elem)) {
                return;
            }
            //privateCache 数据缓存对象的属性data
            //thisCache 数据缓存对象
            //如果pvt为true  privateCache与thisCache都是指向数据缓存对象
            var privateCache, thisCache, ret,
                internalKey = jQuery.expando,//jQuery21004586855585473266
                getByName = typeof name === "string",

                //处理Dom元素的数据问题,防止内存泄露
                isNode = elem.nodeType,//1-9 Dom元素
                //缓存的数据来源:Dom元素需要使用jQuery的cache,其他元素不需要
                cache = isNode ? jQuery.cache : elem,
                id = isNode ? elem[internalKey] : elem[internalKey] && internalKey,//Dom节点第一次存数据是没有id的
                isEvents = name === "events";
            //如果是读取数据,但是没有数据,则直接返回
            //后面getByName&&data===undefined 如果name是字符串、data是undefined则说明是在读取数据
            //前面其他的 如果关联id不存在,说明没有数据;如果cache[id] 不存在,也说明没有数据,如果是读取自定义数据,但cache[id].data不存在,说明没有自定义数据
            if ((!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined) {
                return;
            }

            //如果关联id不存在则分配一个
            if (!id) {
                if (isNode) {
                    //仅Dom节点元素需要uuid
                    elem[internalKey] = id = ++jQuery.uuid;
                } else {
                    id = internalKey;
                }
            }
            //如果数据缓存对象不存在,则初始化它
            if (!cache[id]) {
                cache[id] = {};
                //避免非Dom节点对象被JSON.stringfy序列化的时候,暴露缓存数据
                if (!isNode) {
                    cache[id].toJSON = jQuery.noop; //jQuery.noop 即 function(){}
                }
            }

            //传入一个对象或者函数而不是键值对,则批量设置数据
            if (typeof name === "object" || typeof name === "function") {
                if (pvt) {//内部数据
                    cache[id] = jQuery.extend(cache[id], name);
                } else {
                    cache[id].data = jQuery.extend(cache[id].data, name);
                }
            }
            //先都指向数据缓存对象=====>说明以上都是在处理id问题
            privateCache = thisCache = cache[id];

            if (!pvt) {
                //如果自定数据缓存对象cache[id].data不存在,则先将其初始化空对象
                if (!thisCache.data) {
                    thisCache.data = {};
                }
                thisCache = thisCache.data;
            }
            //如果参数data不是undefined,则把参数data设置到属性name上
            if (data !== undefined) {
                //这时thisCache已经为privateCache.data了,现在只是在data上加属性值了
                thisCache[jQuery.camelCase(name)] = data;
            }

            //特殊处理events,如果参数name是字符串events,并且未设置自定义数据events,则返回事件缓存对象,在其中存储了事件监听函数
            if (isEvents && !thisCache[name]) {
                return privateCache.events;
            }
            //如果参数name是字符串,则读取单个数据
            //在这个过程中,会先尝试读取参数name对应的数据,如果未取到,则把参数name转换为驼峰式再次尝试读取
            //所以如果你使用 $.data(elem,name,data)的时候,会返回data
            if (getByName) {
                ret = thisCache[name];

                if (ret == null) {
                    ret = thisCache[jQuery.camelCase(name)];
                }
            } else {
                //如果未传入参数name、data,则返回数据缓存对象
                ret = thisCache;
            }

            return ret;
        },
        removeData: function (elem, name, pvt/*仅jQuery内部使用*/) {
            if (!jQuery.acceptData(elem)) {
                return;
            }

            var thisCache, i, l,
                internalKey = jQuery.expando,
                isNode = elem.nodeType,
                cache = isNode ? jQuery.cache : elem,
                id = isNode ? elem[internalKey] : internalKey;

            //当cache[id]没有数据时,即缓存中没有数据的时候,就直接返回
            if (!cache[id]) {
                return;
            }

            if (name) {
                thisCache = pvt ? cache[id] : cache[id].data;

                if (thisCache) {
                    if (!jQuery.isArray(name)) {
                        //当name 不是数组的时候
                        if (name in thisCache) {
                            name = [name];
                        } else {
                            name = jQuery.camelCase(name);
                            if (name in thisCache) {
                                name = [name];
                            } else {
                                //name是这个形式 "age name"
                                name = name.split(" ");
                            }
                        }
                    }
                    //批量删除
                    for (i = 0, l = name.length; i < l; i++) {
                        delete thisCache[name[i]];
                    }
                    //判断删除后的缓存数据是不是空对象
                    if (!(pvt ? isEmptyDataObject : jQuery.isEmptyObject)(thisCache)) {
                        return;
                    }
                }
            }

            if (!pvt) {
                //当缓存数据为空对象的时候
                delete cache[id].data;

                if (!isEmptyDataObject(cache[id])) {
                    return;
                }
            }

            //确保cache不是window对象
            if (jQuery.support.deleteExpando || !cache.setInterval) {
                delete cache[id];
            } else {
                cache[id] = null;
            }

            //当为Dom节点情况的时候,到这里说明Dom节点上的数据已经为空了   所以要删除Dom上的id值
            if (isNode) {
                if (jQuery.support.deleteExpando) {
                    delete elem[internalKey];
                } else if (elem.removeAttribute) {
                    elem.removeAttribute(internalKey);
                } else {
                    elem[internalKey] = null;
                }
            }
        },
        //仅jQuery内部使用
        _data: function (elem, name, data) {
            return jQuery.data(elem, name, data, true);
        },
        //判断Dom节点是否可以保存data expando信息
        acceptData: function (elem) {
            if (elem.nodeName) { //这里可以知道elem不是jquery对象,它是一个Dom元素或者普通对象等
                var match = jQuery.noData[elem.nodeName.toLowerCase()];
                if (match) {
                    return !(match === true || elem.getAttribute("classid") !== match);
                }
            }
            return true;
        }
    });

    //对jQuery实例的扩展
    jQuery.fn.extend({
        data: function (key, value) {
            var parts, attr, name,
                data = null;

            if (typeof key === "undefined") {
                if (this.length) {
                    //jQuery对象转换为普通对象,调用jQuery.data
                    data = jQuery.data(this[0]);

                    //当为Dom节点的时候处理方式
                    if (this[0].nodeType === 1 && !jQuery._data(this[0], "parsedAttrs")) {
                        attr = this[0].attributes;
                        for (var i = 0, l = attr.length; i < l; i++) {
                            name = attr[i].name;
                            //html5属性标签 data-***
                            if (name.indexOf("data-") === 0) {
                                name = jQuery.camelCase(name.substring(5));
                                dataAttr(this[0], name, data[name]);
                            }
                        }
                        jQuery._data(this[0], "parsedAttrs", true);
                    }
                }
                return data;
            } else if (typeof key === "object") {
                return this.each(function () {
                    jQuery.data(this, key);
                });
            }
            //为触发自带事件做准备
            parts = key.split(".");
            parts[1] = parts[1] ? "." + parts[1] : "";

            if (value === undefined) {
                data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

                if (data === undefined && this.length) {
                    data = jQuery.data(this[0], key);
                    data = dataAttr(this[0], key, data);
                }

                return data === undefined && parts[1] ? this.data(parts[0]) : data;
            } else {
                //jQuery常用的处理方式,因为jQuery对象其实就是一个类数组,故需要用each
                return this.each(function () {
                    var self = jQuery(this),
                        args = [parts[0], value];

                    self.triggerHandler("setData" + parts[1] + "!", args);
                    jQuery.data(this, key, value);
                    self.triggerHandler("changeData" + parts[1] + "!", args);
                });
            }

        },
        removeData: function (key) {
            //jQuery常用的处理方式,因为jQuery对象其实就是一个类数组,故需要用each
            return this.each(function () {
                jQuery.removeData(this, key);
            });
        }
    });

    function dataAttr(elem, key, data) {
        //如果通过internal没有找到,则去根据 HTML5中的data-* 属性来
        if (data === undefined && elem.nodeType === 1) {

            var name = "data-" + key.replace(rmultiDash, "-$1").toLowerCase();
            data = elem.getAttribute(name);

            if (typeof data === "string") {
                try{
                    data = data === "true" ? true :
                        data === "false" ? false :
                        data === "null" ? null :
                        jQuery.isNumeric(data) ? parseFloat(data) :
                        rbrace.test(data) ? jQuery.parseJSON(data) :
                        data;
                } catch (e) { }

                jQuery.data(elem, key, data);
            } else {
                data = undefined;
            }
        }
        return data;
    }

    //检查一个缓冲对象是否为空对象
    function isEmptyDataObject(obj) {
        for (var name in obj) {
            //如果公共的数据对象为空,那么私有的必然也是为空
            //  isEmptyObject: function( obj ) {
            //    for ( var name in obj ) {
            //        return false;
            //    }
            //    return true;
            //}
            if (name === "data" && jQuery.isEmptyObject(obj[name])) {
                continue;
            }
            //会附加一个toJSON属性  jQuery.noop
            if (name !== "toJSON") {
                return false;
            }
        }
        return true;
    }
})(jQuery)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值