美文网首页
JQ第八部分源码学习

JQ第八部分源码学习

作者: 没了提心吊胆的稗子 | 来源:发表于2019-07-24 13:16 被阅读0次

    DOM元素与对象之间互相引用,大部分浏览器会出现内存泄漏

    var oDiv = document.getElementById('box');
    var obj = {};
    
    oDiv.name = obj;
    obj.age = oDiv;
    // attr prop方式都会出现这种问题
    
    
    $('#box').data('name-age', 'hello');
    
    // 会先看cache有没有nameAge属性,若没有就设置为驼峰形式
    // 若有就设置成name-age形式,但原有的驼峰属性的属性值会被当前设置的这个覆盖
    this.cache = {
      1: {
        'nameAge': 'hello'
       }
    }
    

    JQ中的data数据缓存就是解决这个内存泄漏的问题的,当元素要引用对象的时候,利用一个中介cache。

    // 数据缓存
    var data_user, data_priv,
        rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
        rmultiDash = /([A-Z])/g;
    
    function Data() {
        // Support: Android < 4,
        // Old WebKit does not have Object.preventExtensions/freeze method,
        // return new empty object instead with no [[set]] accessor
        // 属性为0的,不会设置值 即非元素节点或者document不允许设置data
        Object.defineProperty( this.cache = {}, 0, {
            get: function() {
                return {};
            }
        });
    
        // expando是JQ版本加随机数  这里又再加上了一串随机数 唯一标识
        // 用来表示设置data的属性名
        this.expando = jQuery.expando + Math.random();
    }
    
    Data.uid = 1;
    
    // 判断节点类型
    Data.accepts = function( owner ) {
        // Accepts only:
        //  - Node
        //    - Node.ELEMENT_NODE
        //    - Node.DOCUMENT_NODE
        //  - Object
        //    - Any
        // 元素节点或者document返回真,否则返回假
        return owner.nodeType ?
            owner.nodeType === 1 || owner.nodeType === 9 : true;
    };
    
    Data.prototype = {
        key: function( owner ) {
            // We can accept data for non-element nodes in modern browsers,
            // but we should not, see #8335.
            // Always return the key for a frozen object.
            // 非元素节点或者document时 返回0
            if ( !Data.accepts( owner ) ) {
                return 0;
            }
    
            var descriptor = {},
                // Check if the owner object already has a cache key
                // owner就是当前要添加data属性的元素
                unlock = owner[ this.expando ];
    
            // If not, create one
            if ( !unlock ) {
                unlock = Data.uid++;
    
                // Secure it in a non-enumerable, non-writable property
                try {
                    descriptor[ this.expando ] = { value: unlock };
                    Object.defineProperties( owner, descriptor );
    
                // Support: Android < 4
                // Fallback to a less secure definition
                } catch ( e ) {
                    descriptor[ this.expando ] = unlock;
                    jQuery.extend( owner, descriptor );
                }
            }
    
            // Ensure the cache object
            if ( !this.cache[ unlock ] ) {
                this.cache[ unlock ] = {};
            }
    
            return unlock;
        },
        set: function( owner, data, value ) {
            var prop,
                // There may be an unlock assigned to this node,
                // if there is no entry for this "owner", create one inline
                // and set the unlock as though an owner entry had always existed
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];
    
            // Handle: [ owner, key, value ] args
            // 直接赋值
            if ( typeof data === "string" ) {
                cache[ data ] = value;
    
            // Handle: [ owner, { properties } ] args
            // 若添加的属性是对象 就要循环遍历
            } else {
                // Fresh assignments by object are shallow copied
                if ( jQuery.isEmptyObject( cache ) ) {
                    // extend内部也是循环呀
                    jQuery.extend( this.cache[ unlock ], data );
                // Otherwise, copy the properties one-by-one to the cache object
                } else {
                    for ( prop in data ) {
                        cache[ prop ] = data[ prop ];
                    }
                }
            }
            return cache;
        },
        get: function( owner, key ) {
            // Either a valid cache is found, or will be created.
            // New caches will be created and the unlock returned,
            // allowing direct access to the newly created
            // empty data object. A valid owner object must be provided.
            var cache = this.cache[ this.key( owner ) ];
    
            return key === undefined ?
                cache : cache[ key ];
        },
        // data
        access: function( owner, key, value ) {
            var stored;
            // 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) ) {
    
                stored = this.get( owner, key );
    
                return stored !== undefined ?
                    stored : this.get( owner, jQuery.camelCase(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: function( owner, key ) {
            var i, name, camel,
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];
    
            // 什么都不传就默认清空所有
            if ( key === undefined ) {
                this.cache[ unlock ] = {};
    
            } else {
                // Support array or space separated string of keys
                if ( jQuery.isArray( key ) ) {
                    // 可以传递一个数组进来
                    // If "name" is an array of keys...
                    // When data is initially created, via ("key", "val") signature,
                    // keys will be converted to camelCase.
                    // Since there is no way to tell _how_ a key was added, remove
                    // both plain key and camelCase key. #12786
                    // This will only penalize the array argument path.
                    name = key.concat( key.map( jQuery.camelCase ) );
                } else {
                    camel = jQuery.camelCase( key );
                    // Try the string as a key before any manipulation
                    if ( key in cache ) {
                        name = [ key, camel ];
                    } else {
                        // If a key with the spaces exists, use it.
                        // Otherwise, create an array by matching non-whitespace
                        name = camel;
                        // 删除一个不存在属性时就不作处理 返回一个空数组
                        name = name in cache ?
                            [ name ] : ( name.match( core_rnotwhite ) || [] );
                    }
                }
    
                i = name.length;
                while ( i-- ) {
                    delete cache[ name[ i ] ];
                }
            }
        },
        hasData: function( owner ) {
            return !jQuery.isEmptyObject(
                this.cache[ owner[ this.expando ] ] || {}
            );
        },
        discard: function( owner ) {
            if ( owner[ this.expando ] ) {
                delete this.cache[ owner[ this.expando ] ];
            }
        }
    };
    
    // These may be used throughout the jQuery core codebase
    data_user = new Data();
    data_priv = new Data();
    
    
    jQuery.extend({
        acceptData: Data.accepts,
    
        hasData: function( elem ) {
            return data_user.hasData( elem ) || data_priv.hasData( elem );
        },
    
        data: function( elem, name, data ) {
            return data_user.access( elem, name, data );
        },
    
        removeData: function( elem, name ) {
            data_user.remove( elem, name );
        },
    
        // TODO: Now that all calls to _data and _removeData have been replaced
        // with direct calls to data_priv methods, these can be deprecated.
        _data: function( elem, name, data ) {
            return data_priv.access( elem, name, data );
        },
    
        _removeData: function( elem, name ) {
            data_priv.remove( elem, name );
        }
    });
    
    // 获取只会返回第一个
    jQuery.fn.extend({
        data: function( key, value ) {
            // 先保存第一项
            var attrs, name,
                elem = this[ 0 ],
                i = 0,
                data = null;
    
            // Gets all values key不传值会return所有的值
            if ( key === undefined ) {
                if ( this.length ) {
                    data = data_user.get( elem );
    
                    if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
                        // 所有属性的集合
                        attrs = elem.attributes;
                        for ( ; i < attrs.length; i++ ) {
                            name = attrs[ i ].name;
                            // 用data-方式添加的属性
                            if ( name.indexOf( "data-" ) === 0 ) {
                                name = jQuery.camelCase( name.slice(5) );
                                dataAttr( elem, name, data[ name ] );
                            }
                        }
                        data_priv.set( elem, "hasDataAttrs", true );
                    }
                }
    
                return data;
            }
    
            // Sets multiple values
            if ( typeof key === "object" ) {
                return this.each(function() {
                    data_user.set( this, key );
                });
            }
    
            return jQuery.access( this, function( value ) {
                var data,
                    camelKey = jQuery.camelCase( key );
    
                // 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 ) {
                    // Attempt to get data from the cache
                    // with the key as-is
                    data = data_user.get( elem, key );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // Attempt to get data from the cache
                    // with the key camelized
                    data = data_user.get( elem, camelKey );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // Attempt to "discover" the data in
                    // HTML5 custom data-* attrs
                    data = dataAttr( elem, camelKey, undefined );
                    if ( data !== undefined ) {
                        return data;
                    }
    
                    // We tried really hard, but the data doesn't exist.
                    return;
                }
    
                // Set the data...
                this.each(function() {
                    // First, attempt to store a copy or reference of any
                    // data that might've been store with a camelCased key.
                    var data = data_user.get( this, camelKey );
    
                    // For HTML5 data-* attribute interop, we have to
                    // store property names with dashes in a camelCase form.
                    // This might not apply to all properties...*
                    data_user.set( this, camelKey, value );
    
                    // *... In the case of properties that might _actually_
                    // have dashes, we need to also store a copy of that
                    // unchanged property.
                    if ( key.indexOf("-") !== -1 && data !== undefined ) {
                        data_user.set( this, key, value );
                    }
                });
            }, null, value, arguments.length > 1, null, true );
        },
    
        removeData: function( key ) {
            return this.each(function() {
                data_user.remove( this, key );
            });
        }
    });
    
    // h5的方式
    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 ) {
            // "-$1"正则中的第一个子项
            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 :
                        // Only convert to a number if it doesn't change the string
                        // 字符串数字存为数字
                        +data + "" === data ? +data :
                        // rbrace表示{}[]的正则,
                        rbrace.test( data ) ? JSON.parse( data ) :
                        data;
                } catch( e ) {}
    
                // Make sure we set the data so it isn't changed later
                data_user.set( elem, key, data );
            } else {
                data = undefined;
            }
        }
        return data;
    }
    

    相关文章

      网友评论

          本文标题:JQ第八部分源码学习

          本文链接:https://www.haomeiwen.com/subject/wlxvlctx.html