zoukankan      html  css  js  c++  java
  • jQuery toggleClass 源码解读

        toggleClass: function( value, stateVal ) {
            var type = typeof value;//值类型
    
            if ( typeof stateVal === "boolean" && type === "string" ) {//如果第二个参数为bool值
                return stateVal ? this.addClass( value ) : this.removeClass( value );//如果第二个参为true,添加class,否则移除class
            }
    
            if ( jQuery.isFunction( value ) ) {//如果第一个参数是函数
                return this.each(function( i ) {
                    jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
                });
            }
    
            return this.each(function() {
                if ( type === "string" ) {
                    // toggle individual class names
                    var className,
                        i = 0,
                        self = jQuery( this ),
                        classNames = value.match( rnotwhite ) || [];//将按空格分隔的className分割成数组
    
                    while ( (className = classNames[ i++ ]) ) {//遍历数组
                        // check each className given, space separated list
                        if ( self.hasClass( className ) ) {
                            self.removeClass( className );
                        } else {
                            self.addClass( className );
                        }
                    }
    
                // Toggle whole class name 如果没有传入第一个参数或者第一个参数是undefined或者第一个参数是bool值
                } else if ( type === strundefined || type === "boolean" ) {
                    if ( this.className ) {
                        // store className if set
                        data_priv.set( this, "__className__", this.className );
                    }
    
                    // If the element has a class name or if we're passed "false",
                    // then remove the whole classname (if there was one, the above saved it).
                    // Otherwise bring back whatever was previously saved (if anything),
                    // falling back to the empty string if nothing was stored.
                    this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
                }
            });
        }

    前面几种情况都很好理解,最后一种$("#xxx").toggleClass()则用到了data_priv对象,因为它需要将移除的className缓存起来。

    data_priv是jQuery内部的缓存对象,在初始化时,它会生成一个空的cache对象用于缓存数据,还有根据jQuery.expando生成它自身的expando,此外,它还有一些方法

    在调用data_priv.set()时,会首先调用data_priv.key(),因为它会在elem上通过defineProperties()为elem添加一个expano的值

    比如:

    之后data_priv.set中就可以在data_priv.cache中存储值:

    在调用data_priv.get()时,首先也是调用data_priv.key()方法,用它来返回elem中expando的值,也就是cache中对应的键,然后就能找到存储的值了。

    data_priv的源码如下:

    function Data() {
        // Support: Android<4,
        // Old WebKit does not have Object.preventExtensions/freeze method,
        // return new empty object instead with no [[set]] accessor
        Object.defineProperty( this.cache = {}, 0, {//初始化定义this.cache为{}
            get: function() {
                return {};
            }
        });
        this.expando = jQuery.expando + Data.uid++;//jQuery21301509336824528871 
    }
    
    Data.uid = 1;
    Data.accepts = jQuery.acceptData;
    
    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.
            if ( !Data.accepts( owner ) ) {
                return 0;
            }
    
            var descriptor = {},
                // Check if the owner object already has a cache key
                
                 unlock = owner[ this.expando ];
            // If not, create one
            if ( !unlock ) {
                unlock = Data.uid++;//3
    
                // Secure it in a non-enumerable, non-writable property
                try {
                    descriptor[ this.expando ] = { value: unlock };
                    Object.defineProperties( owner, descriptor );
                    //console.log(owner[this.expando]);//3
    
                // 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 ] ) {//undefined
                this.cache[ unlock ] = {};
            }
            return unlock;//3
        },
        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 ),//3
                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 ) ) {
                    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 ];
        },
        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( 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 ] ];
            }
        }
    };
    var data_priv = new Data();
    var data_user = new Data();
  • 相关阅读:
    Deferred对象
    回流和重绘(转载)
    Javascript数组与类数组对象
    函数节流与函数去抖
    Express框架Fetch通信
    nodejs调试总结
    Webpack vs Gulp(转载)
    sourcetree管理git
    js时间转化
    React封装RadioGroup
  • 原文地址:https://www.cnblogs.com/qianlegeqian/p/4309067.html
Copyright © 2011-2022 走看看