• JQuery源码解析-JQuery的工具方法(5)


      下面对最后这几个方法进行讲解。

      guid():唯一表示(内部)

      proxy():改变this指向

      access(): 多功能值操作

      now():当前时间

      swap():css交换(内部)

    guid:

    这个属性是对事件进行控制的,例如每次对dom元素进行绑定事件的时候,会通过这个属性进行绑定,这个属性每次自增,产生一个唯一的标示,所以对dom元素进行事件解绑等操作的时候,通过这个属性就可以找到。

    源码:

    // A global GUID counter for objects
        guid: 1,

    proxy方法:

    这个方法是用来改变方法的内部指向,例如:

     function show() {
                console.log(this);
            }
    
            $.proxy(show, document)();  //document

    不通过这个方法直接调用show方法,那么this指向的是window,可以看到,通过proxy方法改变后,this指向是document。

    当需要向方法里传参时,可以这么写:

       function show(a ,b ) {
                console.log(a,b,this);
            }
    
            $.proxy(show, document,1)(2);  //1 2 document

    可以看到参数传递既可以在proxy方法内部,从第三个参数依次传递,也可以通过括号内部将参数传入,还可以分开传入。这么做的原因是通过这个方法可以进行科里化,对某个参数进行绑定。

    另外源码内部还支持另一种方式进行参数传递:

      var obj={
                show: function () {
                    console.log(this);
                }
            }
            $.proxy(obj, 'show')(); //Object {}

    可以看到通过这种方式,也可改变方法的指向。

    下面看一下源码:

    // Bind a function to a context, optionally partially applying any
        // arguments.
        proxy: function( fn, context ) {
            var tmp, args, proxy;
    
            if ( typeof context === "string" ) {
                tmp = fn[ context ];
                context = fn;
                fn = tmp;
            }
    
            // Quick check to determine if target is callable, in the spec
            // this throws a TypeError, but we will just return undefined.
            if ( !jQuery.isFunction( fn ) ) {
                return undefined;
            }
    
            // Simulated bind
            args = core_slice.call( arguments, 2 );
            proxy = function() {
                return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
            };
    
            // Set the guid of unique handler to the same of original handler, so it can be removed
            proxy.guid = fn.guid = fn.guid || jQuery.guid++;
    
            return proxy;
        },

    首先是对参数为string类型的情况进行处理,也就是上面说的另一种方式调用。可以看到通过:

      if ( typeof context === "string" ) {
                tmp = fn[ context ];
                context = fn;
                fn = tmp;
            }

    这段代码的转换后,实际上参数还是被转换成了下面这种调用方式。

     $.proxy(obj.show, obj)();

    下面对参数进行判断,如果fn不是方法,那么直接返回undefined,也没有往下执行的必要了。

    // Quick check to determine if target is callable, in the spec
            // this throws a TypeError, but we will just return undefined.
            if ( !jQuery.isFunction( fn ) ) {
                return undefined;
            }

    下面是对参数进行处理,并改变方法的作用域:

    // Simulated bind
            args = core_slice.call( arguments, 2 );
            proxy = function() {
                return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
            };

    先是获取$.proxy(show, document,1) 这里的参数,可以看到,从第二个参数以后开始截取,因为前两个参数是固定的。

    然后在proxy方法中返回传入的参数,并调用apply改变作用域,经过下面这句的处理,也就是可以对分开传参这种方式进行支持了。

    args.concat( core_slice.call( arguments ) )

    先将$.proxy(show, document,1)这里的参数获取到,然后在调用的时候,$.proxy(show, document,1)(2)在将方法括号内部的参数获取,并进行连接。传入方法就可以了。

    // Set the guid of unique handler to the same of original handler, so it can be removed
            proxy.guid = fn.guid = fn.guid || jQuery.guid++;
    
            return proxy;

    可以看到当处理完方法后,把方法的guid属性进行自增。

    最后返回proxy,实际上也就是传入的方法指向,所以这个方法不是自动运行的,想要调用,还需要在后面加上一对括号。

    access方法:

    这个方法是内部使用的获取或赋值操作的一个公共方法,例如jQuery的css方法,attr方法都会调用这个方法,我们在调用这些方法时,当传入一个参数的时候,是取值操作,例如:

    $('#div1').css('width')

    当传入两个参数的时候,是赋值操作,例如:

    $('#div1').css('width','200px')

    当传入一个json对象的时候,是设置多个属性,例如:

    $('#div1').css({ 'width': '200px', 'height': '200px' })

    那么这些都怎么做到的呢,在一个方法里可以实现取值赋值的,其实在各自的方法中,例如css(),attr()这些方法中,都是对access方法进行调用,并通过回调方法,来实现各自特有的逻辑,看一下源码:

    // Multifunctional method to get and set values of a collection
        // The value/s can optionally be executed if it's a function
        access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
            var i = 0,
                length = elems.length,
                bulk = key == null;
    
            // Sets many values
            if ( jQuery.type( key ) === "object" ) {
                chainable = true;
                for ( i in key ) {
                    jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
                }
    
            // Sets one value
            } else if ( value !== undefined ) {
                chainable = true;
    
                if ( !jQuery.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 < length; i++ ) {
                        fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
                    }
                }
            }
    
            return chainable ?
                elems :
    
                // Gets
                bulk ?
                    fn.call( elems ) :
                    length ? fn( elems[0], key ) : emptyGet;
        },

    可以看到,这个方法接收很多参数,elems表示节点元素,fn是回调方法,key是属性名,比如 width等,value是对应的值,比如100px,chainable是用来区分取值还是赋值。赋值为true,取值为false,通过css方法的arguments.length > 1,来进行判断。

    大于一个就是赋值,否则就是取值:

     return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );

    接下来定义变量:

            var i = 0,
                length = elems.length,
                bulk = key == null;

    i:是用来循环的索引,length来保存传入元素的长度,后面判断用,bulk来判断是否传入key这个变量,如果未传,则为true。

    接着往下看:

    // Sets many values
            if ( jQuery.type( key ) === "object" ) {
                chainable = true;
                for ( i in key ) {
                    jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
                }

    从注释就可以看到,这里是对多个属性进行赋值,当传入的参数为object时,也就是json对象时,会执行这里。

    先将chainable这个值赋值true,因为只传一个json对象是,那么必然不满足arguments>1这个条件,所以这里将其手动改变一下。

    然后遍历这个对象,对json进行拆解之后,对当前方法进行递归。

    接下来看一下赋值单个属性的操作:

     else if ( value !== undefined ) {
                chainable = true;
    
                if ( !jQuery.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 < length; i++ ) {
                        fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
                    }
                }
            }

    首先对value进行判断,当value不为undefined时,也就代表了赋值操作,避免漏传chainable参数的情况。然后对chainable进行手动更改。

    接着判断value是否方法,在如果是的话,则表示:

    attr( attributeName, function(index, attr) )

    可能是这种方式调用的,平时几乎没使用过这种情况。

    接下来判断是否有属性值,在对raw进行判断,也就是,如果value不是function,直接回调并传入节点元素和value。

    如果value是function的话,则将外层包一层方法(这里没太明白具体原因,平时使用的时候一般不会执行这个判断

    接下来判断fn是否为空,然后依次把拆解之后的参数传入回调方法。

    再来看最后一部分:

        return chainable ?
                elems :
    
                // Gets
                bulk ?
                    fn.call( elems ) :
                    length ? fn( elems[0], key ) : emptyGet;

    如果cahinable为true也就是赋值的时候,直接返回这个元素,方便链式调用,否则就是取值操作,在这里又是一个判断,当bulk为true的时候,代表key为null则直接把元素传给回调方法。

    如果有key值在判断节点的长度,如果有长度,则第一个元素和key值返回,否则返回一个undefined。

    这个方法以后到那些赋值取值的方法时,在仔细说明,现在只看这个方法,可能不是很清晰。

    now属性:

        now: Date.now,

    这个属性没什么好说的,直接返回当前时间。

     swap方法:

    这个方法是用作css交换的。例如:

     <div id="div1" style="100px;height:100px;background:red;display:none">1111</div>
            $(function () {
                console.log($('#div1').css('width')); //100px
                console.log($('#div1').get(0) .offsetWidth); //0
            })

    从结果可以看出,原生js无法获取display为none的属性值,而jQuery却可以获取,原因就是在内部使用了这个方法。

    方法主要将div的样式转换成:

     <div id="div1" style="100px;height:100px;background:red;display:block;visibility:hidden;position:absolute;">1111</div>

    获取到需要的值之后,在将其css属性还原成css属性。源码:

    // A method for quickly swapping in/out CSS properties to get correct calculations.
        // Note: this method belongs to the css module but it's needed here for the support module.
        // If support gets modularized, this method should be moved back to the css module.
        swap: function( elem, options, callback, args ) {
            var ret, name,
                old = {};
    
            // Remember the old values, and insert the new ones
            for ( name in options ) {
                old[ name ] = elem.style[ name ];
                elem.style[ name ] = options[ name ];
            }
    
            ret = callback.apply( elem, args || [] );
    
            // Revert the old values
            for ( name in options ) {
                elem.style[ name ] = old[ name ];
            }
    
            return ret;
        }

    代码主要为三部分,第一部分:

     // Remember the old values, and insert the new ones
            for ( name in options ) {
                old[ name ] = elem.style[ name ];
                elem.style[ name ] = options[ name ];
            }

    这部分主要将原有的样式存到old对象中,然后将新的样式插入到节点中。

    第二部分:

        ret = callback.apply( elem, args || [] );

    获取到需要的值。

    第三部分:

    // Revert the old values
            for ( name in options ) {
                elem.style[ name ] = old[ name ];
            }

    将原有的样式还原。

    下面在来说明一下isArraylike这个方法,这个方法在前面文章中用到过,是用来判断是否为数组或类数组的方法。

    源码:

    function isArraylike( obj ) {
        var length = obj.length,
            type = jQuery.type( obj );
    
        if ( jQuery.isWindow( obj ) ) {
            return false;
        }
    
        if ( obj.nodeType === 1 && length ) {
            return true;
        }
    
        return type === "array" || type !== "function" &&
            ( length === 0 ||
            typeof length === "number" && length > 0 && ( length - 1 ) in obj );
    }

    首先获取到参数的长度和类型。如果类型为window的话,做判断的原因是因为window对象在后面的判断中满足,所以提前做处理。直接返回false,如果有nodeType的话并且还有长度,那么就是类数组的形式,返回true。

    如果还不满足则判断是否为数组。

    
    
    
  • 相关阅读:
    vim 常用操作
    Zookeeper 一种简单的原子操作机制:
    用习惯的vimrc配置,在这里记录一下,以后可以继续完善使用
    static_cast, dynamic_cast, const_cast探讨【转】
    常用CSS标签使用
    Java基础
    Hibernate的第一个程序
    Hibernate的优缺点
    python基础语法
    ansible-role安装nginx,keepalived,tomcat
  • 原文地址:https://www.cnblogs.com/y8932809/p/5895708.html
Copyright © 2020-2023  润新知