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();
时间: 2024-10-13 10:57:08

jQuery toggleClass 源码解读的相关文章

jQuery.attr() 源码解读

我们知道,$().attr()实质上是内部调用了jQuery.access方法,在调用时jQuery.attr作为回调传入.在通过种种判断(参看jQuery.access()方法)之后,取值和赋值最后调用了这个jQuery.attr方法. 所以,关键是看jQuery.attr这里怎么走了~~ 源码如下: attr: function( elem, name, value ) { var hooks, ret, nType = elem.nodeType; //如果elem不存在,或者是文本.注释

jQuery addClass() 源码解读

addClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).addClass( value.c

jquery offsetParent()源码解读

offsetParent: function() { return this.map(function() { var offsetParent = this.offsetParent || docElem; while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === &q

jQuery prop() 源码解读

prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes //标签不存在或者是文本.属性.注释节点 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml =

jQuery position() 源码解读

position的代码比较简单... position: function() { if ( !this[ 0 ] ) { return; } var offsetParent, offset, elem = this[ 0 ], parentOffset = { top: 0, left: 0 }; // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only

jQuery.extend()源码解读

// extend方法为jQuery对象和init对象的prototype扩展方法// 同时具有独立的扩展普通对象的功能jQuery.extend = jQuery.fn.extend = function() { /* *target被扩展的对象 *length参数的数量 *deep是否深度操作 */ var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = ar

jQuery源码解读第5章---对Callbacks的解读

jQuery.Callbacks() 是一个多用途的回调函数列表对象 提供了一种强大的方法来管理回调函数队列 先来看看Callbacks的常见的用法 1-------不带参数 先看看不用回调函数的例子 eq function a1(){ console.log('a1') } (function(){ function a2(){ console.log('a2') } })() a1() // a1 a2() //就不行了 这时候我们就可以使用回调函数Callbacks 了 var dfd1

jQuery源码解读第4章---对extend的解读

为什么我们一开始就说extend呢 其实我读源码的过程中,发现其实我们方法就在源码中都调用了extend Callbacks Deferred这些工具方法 所以我们很有必要先学习这些,,,,,,,,,,,这样对我们后续的学习很有帮助 对extend的学习,,,,,首先看下extend我们平时是怎么用的 1...合并对象 extend(dest,src1,src2,,,,,) 它的含义就是将src1,src2......合并到dest中 然后放回的结果就是合并后的dest eq: var dest

vue源码解读预热-0

vueJS的源码解读 vue源码总共包含约一万行代码量(包括注释)特别感谢作者Evan You开放的源代码,访问地址为Github 代码整体介绍与函数介绍预览 代码模块分析 代码整体思路 总体的分析 从图片中可以看出的为采用IIFE(Immediately-Invoked Function Expression)立即执行的函数表达式的形式进行的代码的编写 常见的几种插件方式: (function(,){}(,))或(function(,){})(,)或!function(){}()等等,其中必有