jQueryでのデータ操作方法とjQueryの定義

不言
リリース: 2018-08-04 16:36:07
オリジナル
2088 人が閲覧しました

この記事では、jQuery でのデータ操作の方法と jQuery の定義について説明します。必要な方は参考にしていただければ幸いです。

jQuery にはデータ操作のメソッドが 2 つあります

$().data()

$.data(elem);
ログイン後にコピー

内部実装はカスタム クラス Data から分離できませんData

内部类 Data

Datasrc/data/Data.js定义,构建时为实例添加expando属性,作为唯一标识

function Data() {
    this.expando = jQuery.expando + Data.uid++;
}
ログイン後にコピー

在原型上添加了多个方法

Data.prototype = {
    cache: function(){
        ...
    },
    set: function(){
        ...
    },
    get: function(){
        ...
    },
    access: function(){
        ...
    },
    remove: function(){
        ...
    },
    hasData: function(){
        ...
    }
}
ログイン後にコピー

在jq内部,使用cache方法获取缓存的数据。传入一个参数owner,表示要获取缓存数据的对象。判断在owner上是否有expando属性,如果没有,说明这个owner是否第一次调用,需要在其初始化缓存数据对象。判断节点的类型,如果是元素节点或者document节点或者对象时,可以设置缓存数据。如果是元素节点或者document节点,直接使用对象字面量进行赋值,属性名是expando,值为空对象。如果是对象的话,使用Object.defineProperty为其定义数据,属性名也是expando,初始化为{},同时属性描述符可以更改,不可枚举。

Data.prototype.cache

    cache: function( owner ) {

        // Check if the owner object already has a cache
        // 获取在owner的缓存值
        var value = owner[ this.expando ];

        // If not, create one
        if ( !value ) {
            value = {};

            // We can accept data for non-element nodes in modern browsers,
            // but we should not, see #8335.
            // Always return an empty object.
            // 判断owener类型 是否能在其上调用data
            // 在元素节点或body或对象上可以设置data
            // 其他节点不设置缓存数据
            if ( acceptData( owner ) ) {

                // If it is a node unlikely to be stringify-ed or looped over
                // use plain assignment
                // 此处为owner添加属性 key为Data对象的expando值 建立owner和Data对象之间的连接
                // owner是元素节点或body
                if ( owner.nodeType ) {
                    owner[ this.expando ] = value;

                // Otherwise secure it in a non-enumerable property
                // configurable must be true to allow the property to be
                // deleted when data is removed
                // owner是对象
                // 为owner添加expando属性 初始化为{},同时属性描述符可以更改,不可枚举
                } else {
                    Object.defineProperty( owner, this.expando, {
                        value: value,
                        configurable: true
                    } );
                }
            }
        }

        return value;
    }
ログイン後にコピー

使用set来更新缓存对象,分为data(key,value)data(obj)两种调用情况,保存时要将键名保存为驼峰命名法。

Data.prototype.set

    set: function( owner, data, value ) {
        var prop,
            cache = this.cache( owner );

        // Handle: [ owner, key, value ] args
        // Always use camelCase key (gh-2257)
        if ( typeof data === "string" ) {
            cache[ jQuery.camelCase( data ) ] = value;

        // Handle: [ owner, { properties } ] args
        } else {

            // Copy the properties one-by-one to the cache object
            for ( prop in data ) {
                cache[ jQuery.camelCase( prop ) ] = data[ prop ];
            }
        }
        return cache;
    }
ログイン後にコピー

使用get来获取缓存对象,调用时有data(key)data()。未指定key时直接返回整个cache对象,否则返回cache[key]。键名也要转为驼峰命名。

Data.prototype.get

    get: function( owner, key ) {
        return key === undefined ?
            this.cache( owner ) :

            // Always use camelCase key (gh-2257)
            owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];
    }
ログイン後にコピー

对调用的方式进行区分,内部调用 setget

通过参数的数量和类型进行区分:

  • key为空时,获取整个cache对象

  • key类型为stringvalue===undefined 对应获取指定值

  • 其他调用均为set,在set内部进行区分

Data.prototype.access

    access: function( owner, key, value ) {

        // In cases where either:
        //
        //   1. No key was specified
        //   2. A string key was specified, but no value provided
        //
        // Take the "read" path and allow the get method to determine
        // which value to return, respectively either:
        //
        //   1. The entire cache object
        //   2. The data stored at the key
        //
        if ( key === undefined ||
                ( ( key && typeof key === "string" ) && value === undefined ) ) {

            return this.get( owner, key );
        }

        // When the key is not a string, or both a key and value
        // are specified, set or extend (existing objects) with either:
        //
        //   1. An object of properties
        //   2. A key and value
        //
        this.set( owner, key, value );

        // Since the "set" path can have two possible entry points
        // return the expected data based on which path was taken[*]
        return value !== undefined ? value : key;
    }
ログイン後にコピー

使用remove来删除缓存对象属性,调用时,可以传入一个string,表示要删除的键名,或者传入一个保存多个键名的string数组。键名也要转为驼峰命名。如果不传入出参数,则直接删除掉在owner上的缓存数据对象。

Data.prototype.remove

    remove: function( owner, key ) {
        var i,
            cache = owner[ this.expando ];

        if ( cache === undefined ) {
            return;
        }

        if ( key !== undefined ) {

            // Support array or space separated string of keys
            if ( Array.isArray( key ) ) {

                // If key is an array of keys...
                // We always set camelCase keys, so remove that.
                key = key.map( jQuery.camelCase );
            } else {
                key = jQuery.camelCase( key );

                // If a key with the spaces exists, use it.
                // Otherwise, create an array by matching non-whitespace
                key = key in cache ?
                    [ key ] :
                    ( key.match( rnothtmlwhite ) || [] );
            }

            i = key.length;

            while ( i-- ) {
                delete cache[ key[ i ] ];
            }
        }

        // Remove the expando if there's no more data
        if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

            // Support: Chrome <=35 - 45
            // Webkit & Blink performance suffers when deleting properties
            // from DOM nodes, so set to undefined instead
            // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
            if ( owner.nodeType ) {
                owner[ this.expando ] = undefined;
            } else {
                delete owner[ this.expando ];
            }
        }
    }
ログイン後にコピー

判断owner上是否有缓存数据。

Data.prototype.hasData

    hasData: function( owner ) {
        var cache = owner[ this.expando ];
        return cache !== undefined && !jQuery.isEmptyObject( cache );
    }
ログイン後にコピー

jQuery方法的定义

定义后内部类data后,在/src/data.js进行拓展。在jQuery添加了hasDatadataremoveData_data_removeData等方法,在jQuery.fn上添加了dataremoveData方法。

在jQuery拓展的方法,都是对Data方法的封装,在调用时$.data()时,并无对setget操作区分,在Data.prototype.access内部区分set和get。下面源码中,dataUserdataPrivData实例,分别为缓存数据和表示jq对象是否将元素的data属性添加到dataUser

// $.data
jQuery.extend( {
    hasData: function( elem ) {
        return dataUser.hasData( elem ) || dataPriv.hasData( elem );
    },

    data: function( elem, name, data ) {
        return dataUser.access( elem, name, data );
    },

    removeData: function( elem, name ) {
        dataUser.remove( elem, name );
    },

    // TODO: Now that all calls to _data and _removeData have been replaced
    // with direct calls to dataPriv methods, these can be deprecated.
    _data: function( elem, name, data ) {
        return dataPriv.access( elem, name, data );
    },

    _removeData: function( elem, name ) {
        dataPriv.remove( elem, name );
    }
} );
ログイン後にコピー

jQuery.fn上拓展的方法只有dataremoveData。在data内,先对$().data()$().data({k:v})两个调用情况进行处理。如果是第一种情况,则返回在this[0]上的缓存数据对象,如果是第一次以$().data()的方式调用,同时还会将元素上的data属性添加dataUser中,并更新dataPriv。如果是$().data({k:v})的调用方式,则遍历jq对象,为每个节点更新缓存数据。其他调用方式如$().data(k)$().data(k,v)则调用access进行处理。此处的access并非Data.prototype.access

removeData则遍历jq对象,删除在每个节点上的缓存数据。

// $().data
jQuery.fn.extend( {
    data: function( key, value ) {
        var i, name, data,
            elem = this[ 0 ], // elem为dom对象
            attrs = elem && elem.attributes; // 节点上的属性

        // Gets all values
        // $().data()
        if ( key === undefined ) {
            if ( this.length ) {
                data = dataUser.get( elem );

                // elem是元素节点,且dataPriv中无hasDataAttrs时执行这个代码块里的代码
                // dataPriv上的hasDataAttrs表示elem是否有data-xxx属性
                // 初始化dataPriv后花括号内的代码不再执行,即以下的if内的代码只执行一次
                if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
                    i = attrs.length;
                    while ( i-- ) {

                        // Support: IE 11 only
                        // The attrs elements can be null (#14894)
                        // 将elem的data-*-*属性以*-*转为驼峰命名方式的值为键
                        // 在dataAttr函数内保存到dataUser中
                        // 所以用$().data可以获取元素的data-*属性 但修改后dom上的属性却不变化
                        if ( attrs[ i ] ) {
                            name = attrs[ i ].name;
                            // name为data-xxx
                            if ( name.indexOf( "data-" ) === 0 ) {
                                // name为xxx
                                name = jQuery.camelCase( name.slice( 5 ) );
                                dataAttr( elem, name, data[ name ] );
                            }
                        }
                    }
                    // 同时将dataPriv的hasDataAttrs属性设置为真,表示已经将元素属性节点上的data属性保存到缓存对象中
                    dataPriv.set( elem, "hasDataAttrs", true );
                }
            }

            return data;
        }
        
        // Sets multiple values
        //  $().data(obj) 此处遍历this,即遍历jq对象上所有的节点,并在其设置值
        if ( typeof key === "object" ) {
            return this.each( function() {
                dataUser.set( this, key );
            } );
        }

        // 除了$().data()和$().data({k:v})的其他情况
        return access( this, function( value ) {
            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.
            // value undefined说明是获取操作
            // 调用$().data(k)
            if ( elem && value === undefined ) {

                // Attempt to get data from the cache
                // The key will always be camelCased in Data
                // 从dataUser中获取 非undefined时返回
                data = dataUser.get( elem, key );
                if ( data !== undefined ) {
                    return data;
                }

                // Attempt to "discover" the data in
                // HTML5 custom data-* attrs
                // dataUser中不存在key,调用dataAttr查找元素的data-*属性
                // 如果存在属性,更新dataUser并返回其值
                data = dataAttr( elem, key );
                if ( data !== undefined ) {
                    return data;
                }

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

            // Set the data...
            // jq对象长度 >= 1, 调用如$().data(k,v) 遍历jq对象,为每个节点设置缓存数据
            this.each( function() {

                // We always store the camelCased key
                dataUser.set( this, key, value );
            } );
        }, null, value, arguments.length > 1, null, true );
    },

    // 遍历jq对象,删除各个元素上的缓存数据
    removeData: function( key ) {
        return this.each( function() {
            dataUser.remove( this, key );
        } );
    }
} );
ログイン後にコピー

其中,getData用于对元素上的data属性进行类型转换,dataAttr用于获取保存在元素节点上的data属性,并同时更新dataUser。需要注意的是,以$().data(k, v)方式调用时,如果在缓存数据上查找不到属性,则会调用dataAttr

内部クラス Data

Datasrc/data/Data.js で定義されます。ビルド時に、expando 属性を一意の識別子としてインスタンスに追加します

// 属性值是string 进行类型转换
function getData( data ) {
    if ( data === "true" ) {
        return true;
    }

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

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

    // Only convert to a number if it doesn't change the string
    // data转化成number再转成string后仍严格等于data
    if ( data === +data + "" ) {
        return +data;
    }

    if ( rbrace.test( data ) ) {
        return JSON.parse( data );
    }

    return data;
}

// 获取元素的dataset中的属性,并保存到dataUser中
function dataAttr( elem, key, data ) {
    var name;

    // If nothing was found internally, try to fetch any
    // data from the HTML5 data-* attribute
    // 此处获取dataset里的值
    if ( data === undefined && elem.nodeType === 1 ) {
        name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); // 此处将驼峰命名方式的key转化为data-*-*
        data = elem.getAttribute( name );

        if ( typeof data === "string" ) {
            try {
                data = getData( data );
            } catch ( e ) {}

            // Make sure we set the data so it isn't changed later
            // 将元素的data-*属性保存到dataUser中
            dataUser.set( elem, key, data );
        } else {
            data = undefined;
        }
    }
    return data;
}
ログイン後にコピー
。プロトタイプでは複数のメソッドが追加されています

rrreee jq内ではキャッシュされたデータを取得するためにcacheメソッドを使用します。キャッシュされたデータを取得するオブジェクトを示すパラメータ所有者を渡します。所有者にexpando属性があるかどうかを確認します。存在しない場合は、所有者が初めて呼び出され、キャッシュ データ オブジェクトを初期化する必要があるかどうかを示します。ノードの種類を決定し、要素ノード、ドキュメントノード、オブジェクトの場合、キャッシュデータを設定できます。要素ノードまたはドキュメント ノードの場合、オブジェクト リテラルを直接割り当てに使用します。属性名は Expando で、値は空のオブジェクトです。オブジェクトの場合は、Object.defineProperty を使用してそのデータを定義します。同時に、属性記述子は変更できますが、列挙することはできません。 。

Data.prototype.cache

rrreeeset を使用して、data(key, value)に分割されたキャッシュ オブジェクトを更新します> または data(obj) どちらの呼び出しの場合でも、保存時にキー名をキャメルケースで保存する必要があります。 🎜

Data.prototype.set

rrreee🎜 get を使用してキャッシュ オブジェクトを取得します。呼び出し時に、data(key) と <code>data()。キーが指定されていない場合は、キャッシュ オブジェクト全体が直接返され、それ以外の場合は、cache[key] が返されます。キー名も​​キャメルケースに変換する必要があります。 🎜

Data.prototype.get

rrreee🎜呼び出しメソッドを区別し、内部的に set または get を呼び出します🎜🎜 区別しますパラメータの数とタイプ: 🎜
  • 🎜key が空の場合、キャッシュ オブジェクト全体を取得します 🎜
  • 🎜key のタイプは 文字列ですvalue===unknown は、指定された値の取得に対応します🎜
  • 🎜その他の呼び出しは、setset です。 /code> 内部区別🎜

Data.prototype.access

rrreee🎜キャッシュされたオブジェクト属性を削除するには、remove を使用します。呼び出し時に、削除するキー名を表す文字列を渡すことも、複数のキー名を保持する文字列配列を渡すこともできます。キー名も​​キャメルケースに変換する必要があります。パラメータが渡されたり渡されたりしない場合、所有者のキャッシュされたデータ オブジェクトは直接削除されます。 🎜

Data.prototype.remove

rrreee🎜 所有者にキャッシュされたデータがあるかどうかを確認します。 🎜

Data.prototype.hasData

rrreee🎜jQuery メソッドの定義🎜🎜 内部クラス data を定義した後、 /src/data.js が展開されます。 hasDatadataremoveData_data_removeData およびその他のメソッドを jQuery に追加しました、jQuery.fn に data メソッドと removeData メソッドを追加しました。 🎜🎜 jQuery で拡張されたメソッドはすべて Data メソッドのカプセル化です。$.data() を呼び出す場合、setget はサポートされません。 code>操作の区別。Data.prototype.access 内の set と get を区別します。以下のソース コードでは、dataUserdataPrivData のインスタンスであり、それぞれデータをキャッシュし、jq オブジェクトが要素のデータ属性を追加するかどうかを示します。 jQuery.fn の >dataUser 内の 🎜rrreee🎜 によって拡張されたメソッドは、dataremoveData だけです。 data で、まず $().data()$().data({k:v}) の 2 つの呼び出しを行います。状況は処理されます。最初のケースの場合、 $().data() を初めて呼び出す場合は、 this[0] のキャッシュされたデータ オブジェクトが返されます。また、要素の data 属性を dataUser に追加し、dataPriv を更新します。 $().data({k:v}) の呼び出しメソッドの場合、jq オブジェクトが走査され、キャッシュされたデータがノードごとに更新されます。 $().data(k)$().data(k,v) などの他の呼び出しメソッドは、処理のために access を呼び出します。ここでの accessData.prototype.access ではありません。 🎜🎜removeData は、jq オブジェクトを走査し、各ノードのキャッシュされたデータを削除します。 🎜rrreee🎜 このうち、getData は要素のデータ属性の型変換を行うために使用され、dataAttr は要素ノードに保存されているデータ属性を取得するために使用され、同時に dataUser も更新します。 $().data(k, v) で呼び出すときに、キャッシュされたデータで属性が見つからない場合は、dataAttr が呼び出されることに注意してください。要素 プロパティを検索します。 🎜rrreee🎜おすすめ関連記事: 🎜🎜🎜Angularフォーム検証の2つの方法の紹介🎜🎜

JavaScript関数の使い方?JavaScript関数のプロパティとメソッドの紹介

以上がjQueryでのデータ操作方法とjQueryの定義の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート