jQ的数据管理【后篇】
发布在# 菜鸟解读 jQuery #2014年5月27日view:3529
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

这是给jq元素操作进行的扩展
使用时是这样的: $().data(), $().removeData()
这里小编补充一个知识点,小编还没有介绍过 extend 的用法:
jQuery.extend({});
这种方式添加的对象,所有的api都是可以通过 $.method(),进行调用的
jQuery.fn.extend({});
这种方式添加的对象,所有的api都是可以通过 $().method(),进行调用的
也可以理解为这是为选择dom后的jQ对象添加的api,比如 $().attr(), $().css();

jQuery.fn.extend({
    data: function( key, value ) {
        var parts, part, attr, name, l,
            //取第一个dom元素哦
            elem = this[0],
            i = 0,
            data = null;

        //如果 调用时为指定key,则返回所有的自定义属性报错 data-
        if ( key === undefined ) {
            if ( this.length ) {
                // 返回所有自定义的数据
                data = jQuery.data( elem );
                //如果是元素对象 && 还没有被解析过 
                //到这个if的尾部结束时会添加一个标示
                if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
                    //元素自身的自定义属性
                    attr = elem.attributes;
                    for ( l = attr.length; i < l; i++ ) {
                        name = attr[i].name;
                        //如果发现属性是 data-开头的形式
                        if ( name.indexOf( "data-" ) === 0 ) {
                            //截取后面的并组合成驼峰命名形式
                            name = jQuery.camelCase( name.substring(5) );
                            //把解析后的数据存入到关联的缓存对象中
                            dataAttr( elem, name, data[ name ] );
                        }
                    }
                    //这是一个内置数据属性parsedAttrs,表示该元素被解析过了
                    jQuery._data( elem, "parsedAttrs", true );
                }
            }
            return data;
        }

        //这里被解析为批量设置数据 $().data( {..: ..} );
        if ( typeof key === "object" ) {
            return this.each(function() {
                jQuery.data( this, key );
            });
        }

        parts = key.split( ".", 2 );
        parts[1] = parts[1] ? "." + parts[1] : "";
        part = parts[1] + "!";
        /*
            jQuery.access() 这里暂时不进行介绍,
            对于这里的使用来说,在 access() 内部,只是进行了 fn(value){} 函数的调用,传入的参数是value
            我们直接来看 function( value ) 内部逻辑即可
        */
        return jQuery.access( this, function( value ) {

            if ( value === undefined ) {

                //没有值则是进行读取,这里会触发 自定义事件 getData
                data = this.triggerHandler( "getData" + part, [ parts[0] ] );

                //如果没有返回数据信息,则进行读取数据
                if ( data === undefined && elem ) {
                    data = jQuery.data( elem, key );
                    data = dataAttr( elem, key, data );
                }

                //之前保存了一次.的切割 如果没有取到值,并且有前缀的话,则再次进行获取
                return data === undefined && parts[1] ?
                    this.data( parts[0] ) :
                    data;
            }

            //下面是设置key-val && 触发对应的 setData changeData 事件
            parts[1] = value;
            this.each(function() {
                var self = jQuery( this );
                self.triggerHandler( "setData" + part, parts );
                jQuery.data( this, key, value );
                self.triggerHandler( "changeData" + part, parts );
            });
        }, null, value, arguments.length > 1, null, false );
    },

    removeData: function( key ) {
        return this.each(function() {
            jQuery.removeData( this, key );
        });
    }
});

//这里函数是用来解析dom元素身上的html5 data- 的值
function dataAttr( elem, key, data ) {

    //当传入的data没有值 && 元素时dom节点时
    if ( data === undefined && elem.nodeType === 1 ) {

        //这里是将驼峰模式转成 -拼接模式,比如 userName : user-name
        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 ) ? +data :
                //解析成数组or对象
                rbrace.test( data ) ? jQuery.parseJSON( data ) :
                data;
            } catch( e ) {}
            /*
                这里小编有个质疑,经测试其实很多判断是不必要的,
                "true","false","null","2134", 这些也可以被 JSON.parse() 进行合理的解析
                这里可以写成,为什么jQ要检测如此多的类型呢,小编不解
                try {
                    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 ) {
        //在前篇我们介绍过 数据缓存对象的格式为 {data:{},toJSON:function(){},...}
        //所以当遇到了data时,要去检测下 data对象是否是一个空对象,
        //如果是空就继续运行,那么到下面的if时就会返回false
        if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
            continue;
        }
        //这个属性jQ系统会添加上去,所以这里检测下,
        //即使存在这个属性,也不能认为是非空,所以跳过此项
        if ( name !== "toJSON" ) {
            return false;
        }
    }
    return true;
}

到这里关于data部分小编就解读完毕了,
整体来看的话,小编倒是有个性能上的担忧
通过jQ使用过的对象 ( 绑定事件 || 设置数据 ) 时,
会生成一个对应的数据缓存对象(jQ标示对应的id对应的缓存对象),
如果通过原生方法删除了dom或者复写了dom,
岂不是会造成很多垃圾数据缓存对象在内存($.catch)中,
比如 divElement.innerHTML = "";
如果这种操作量很大的话,也会导致内存升高哦,小编倒是建议大家如果使用jQ,就最好都使用jQ的api进行开发,
以免造成一些不必要的数据留在内存中(小编相信jQ自己的api肯定会对内存有个很好的管理机制)

感谢您的阅读,欢迎留言:提问,交流,斧正

评论
发表评论
4年前

楼主很有毅力,坚持!加油!

WRITTEN BY
前端狮子
JS前端开发工程师 :喜欢研究js,nodejs,html5; 希望结交更多朋友~
TA的新浪微博
PUBLISHED IN
# 菜鸟解读 jQuery #

本栏解读的jQ为1.7.2版本。 本人也是刚开始读起源码,在这里分享下成长的心得。 本人能力有限,也是接触JS不久的初学者,定会有不少解析不全不够明朗【甚至BUG】的地方, 希望各位牛牛多多留言斧正 感谢阅读 ps:由于工作不定时繁忙,本人也无法定期更新,但是会尽量抽时间学习,分享给大家

我的收藏