jquery data方法获取data-*属性值时会转换类型再输出

问题:

jquery data 方法在未缓存过时会从html5的data-*属性获取值,但属性值会转换类型再输出。这会导致一些业务下如果用data-*存入纯数字的字符串key时,再用data取出时会导致类型变化。

举例:

<div id="test" data-id="20220321">test</div>
console.log($('#test').data('id'));  // 20220321 输出Number型
console.log($('#test').attr('data-id'));  // '20220321' 输出String型

解析:

通过源码解析原因,这里以执行$('#test').data('id')为例

以下英文注释为jQuery源码注释,中文注释为本文作者所加

// data方法
        data: function( key, value ) {
// 参数当前状态 key->'id' value->undefined
            var i, name, data,
                elem = this[ 0 ],
                attrs = elem && elem.attributes;

// 这里用来获取所有data缓存,不会执行这里
            // Gets all values
            if ( key === undefined ) {
                if ( this.length ) {
                    data = dataUser.get( elem );

                    if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
                        i = attrs.length;
                        while ( i-- ) {

                            // Support: IE 11 only
                            // The attrs elements can be null (#14894)
                            if ( attrs[ i ] ) {
                                name = attrs[ i ].name;
                                if ( name.indexOf( "data-" ) === 0 ) {
                                    name = camelCase( name.slice( 5 ) );
                                    dataAttr( elem, name, data[ name ] );
                                }
                            }
                        }
                        dataPriv.set( elem, "hasDataAttrs", true );
                    }
                }

                return data;
            }

// 这里用来设置多个data缓存,不会执行这里
            // Sets multiple values
            if ( typeof key === "object" ) {
                return this.each( function() {
                    dataUser.set( this, key );
                } );
            }

// 以上方法都不会执行,然后直接进入access方法
            return access( this, function( value ) {
// 执行access的回调
// 参数当前状态 value->undefined
                var data;

                // The calling jQuery object (element matches) is not empty
                // (and therefore has an element appears at this[ 0 ]) and the
                // `value` parameter was not undefined. An empty jQuery object
                // will result in `undefined` for elem = this[ 0 ] which will
                // throw an exception if an attempt to read a data cache is made.
// 这里用来获取值,会执行
                if ( elem && value === undefined ) {

// 从data缓存中获取,即获取的值需要先用data方法存入
// 因为没有存入过,所以data为undefined
                    // Attempt to get data from the cache
                    // The key will always be camelCased in Data
                    data = dataUser.get( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }
// 进入dataAttr函数,查找data-*属性
                    // Attempt to "discover" the data in
                    // HTML5 custom data-* attrs
                    data = dataAttr( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }

                    // We tried really hard, but the data doesn't exist.
                    return;
                }

                // Set the data...
                this.each( function() {

                    // We always store the camelCased key
                    dataUser.set( this, key, value );
                } );
            }, null, value, arguments.length > 1, null, true );
        },
// access方法
// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
    var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
// elems: jquery实例 fn: 回调函数 key: null value: undefined 
// 参数当前状态 chainable->false emptyGet->null raw->false
        var i = 0,
            len = elems.length,  // 1
            bulk = key == null;  // true (== 是因为 null==undefined 返回true)

// 这里用来设置多个值,不会执行
        // Sets many values
        if ( toType( key ) === "object" ) {
            chainable = true;
            for ( i in key ) {
                access( elems, fn, i, key[ i ], true, emptyGet, raw );
            }
// 这里用来设置单值,不会执行
            // Sets one value

        } else if ( value !== undefined ) {
            chainable = true;

            if ( !isFunction( value ) ) {
                raw = true;
            }

            if ( bulk ) {

                // Bulk operations run against the entire set
                if ( raw ) {
                    fn.call( elems, value );
                    fn = null;

                    // ...except when executing function values
                } else {
                    bulk = fn;
                    fn = function( elem, _key, value ) {
                        return bulk.call( jQuery( elem ), value );
                    };
                }
            }

            if ( fn ) {
                for ( ; i < len; i++ ) {
                    fn(
                        elems[ i ], key, raw ?
                            value :
                            value.call( elems[ i ], i, fn( elems[ i ], key ) )
                    );
                }
            }
        }

        if ( chainable ) {
            return elems;
        }

// 这里用来获取值,会执行
        // Gets
        if ( bulk ) {
// 执行回调,回调代码请返回data方法中查看
            return fn.call( elems );
        }

        return len ? fn( elems[ 0 ], key ) : emptyGet;
    };
// dataAttr函数 
   function dataAttr( elem, key, data ) {
        var name;

        // If nothing was found internally, try to fetch any
        // data from the HTML5 data-* attribute
        if ( data === undefined && elem.nodeType === 1 ) {
// 参数当前状态 rmultiDash-> /[A-Z]/g
// 将驼峰表示法转换为短横杠(html属性不区分大小写)
            name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
// DOM原生方法获取属性值
            data = elem.getAttribute( name );

            if ( typeof data === "string" ) {
                try {
// 进入getData函数,转换类型后输出
                    data = getData( data );
                } catch ( e ) {}

                // Make sure we set the data so it isn't changed later
                dataUser.set( elem, key, data );
            } else {
                data = undefined;
            }
        }
        return data;
    }
// getData函数,类型转换的核心代码
function getData( data ) {
// 因为html属性值不分类型,js取到后都是字符串形式,所以需要js手动转换
// 转Boolean
        if ( data === "true" ) {
            return true;
        }

        if ( data === "false" ) {
            return false;
        }

// 转null
        if ( data === "null" ) {
            return null;
        }

// 转Number
// +data是将data转数字,+""是将数字转字符串
        // Only convert to a number if it doesn't change the string
        if ( data === +data + "" ) {
            return +data;
        }

// 参数当前状态 revrace->/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/
// 这个正则用来匹配对象字符串,但严格的说,不够严谨对于诸如'{test}'、'[test]'这样无法转换为JSON对象的字符串也会通过校验
// 不过介于一般业务中,很少有直接书写对象字符串的情况,并且对不合规范的对象字符串写法确实需要报错提醒。所以这样的正则处理在此处合适,且易于实现
// 对象字符串转对象输出
        if ( rbrace.test( data ) ) {
            return JSON.parse( data );
        }

        return data;
    }

附注:

jQuery官网其实对data方法数据转换的功能做过解释

 所以建议用attr方法获取属性值

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值