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="width: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="width: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。

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

				
时间: 2024-10-26 11:08:08

JQuery源码解析-JQuery的工具方法(5)的相关文章

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

这篇文章主要对下面这几个方法进行解释 error();抛出异常 parseHTML():解析节点 parseJSON():解析JSON parseXML:解析XML noop():空函数 globalEval():全局解析JS camelCase():转驼峰 nodeName():是否为指定节点(内部) error:方法: error方法的作用是抛出一个自定义异常,内部直接调用了原生解释的throw new Error error: function( msg ) { throw new Err

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

这篇对下面的方法进行讲解: isFunction():是否为函数 isArray():是否为数组 isWindow():是否为window isNumeric()是否为数字 type():判断数据类型 isPlainObject():是否为对象自变量 isEmptyObject():是否为空的对象 isFunction方法: 这个方法很简单,判断对象是否为函数,返回bool类型,看一下源码: // See test/unit/core.js for details concerning isFu

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

下面对这些方法进行讲解 each():遍历集合 trim():去前后空格 makeArray():类数组转换真数组 inArray():数组版indexOf merge():合并数组 grep():过滤新数组 map():映射新数组 each方法: each是遍历集合用的方法,也是一个加强版的循环操作.例如: var arr = { Name: 'jam', Age: 15 }; $.each(arr, function (key, val) { console.log(key + ":&quo

jQuery方法源码解析--jQuery($)方法(一)

jQuery方法源码解析--jQuery($)方法 注: 1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可 2.转载请注明出处 jQuery方法: 这个方法大家都不陌生,在使用过程中,它还有另外一个名字,美元符号:$,$(...)其实就是jQuery(...); 它有很多种用法,通常都返回一个jquery对象,也可以作为$(document).ready(...);的简写形式,分析之前先看一下jQuery都有什么用法. 1.jQuery( selector [, co

十七.jQuery源码解析之入口方法Sizzle(1)

函数Sizzle(selector,context,results,seed)用于查找与选择器表达式selector匹配的元素集合.该函数是选择器引擎的入口. 函数Sizzle执行的6个关键步骤如下: 1.解析选择器表达式,解析出块表达式和关系符. 2.如果存在位置伪类,则从左向右查找: a.查找第一个块表达式匹配的元素集合,得到第一个上下文元素集合. b.遍历剩余的块表达式和块间关系符,不断缩小上下文元素集合. 3.否则从右向左查找: a.查找最后一个块表达式匹配的元素集合,得到候选集,映射集

jQuery源码解析(jQuery对象的实例属性和方法)

1.记录版本号 以及 修正constructor指向 jquery: core_version, constructor: jQuery, 因为jQuery.prototype={ ... } 这种写法将自动生成的jQuery.prototype.constructor属性覆盖删除,所以需要重新修正(指向其构造函数 jQuery).2.init() 初始化方法: init()方法最终返回的是this -jQuery(其实是jQuery.prototype.init)方法的实例对象. 实例对象继承

jquery源码解析:代码结构分析

本系列是针对jquery2.0.3版本进行的讲解.此版本不支持IE8及以下版本. (function(){ (21, 94)     定义了一些变量和函数,   jQuery = function(){}; (96,283)   给jQuery对象添加一些属性和方法(实例方法,通过$("div")这类的jQuery实例对象来调用) (285,347)   extend : jQuery的继承方法 (349,817)   jQuery.extend():扩展一些工具方法(静态方法,直接通

五.jQuery源码解析之jQuery.extend(),jQuery.fn.extend()

给jQuery做过扩展或者制作过jQuery插件的人这两个方法东西可能不陌生.jQuery.extend([deep],target,object1,,object2...[objectN]) jQuery.fn.extend([deep],target,object1,,object2...[objectN])这两个属性都是用于合并两个或多个对象的属性到target对象.deep是布尔值,表示是否进行深度合并,默认是false,不执行深度合并.通过这种方式可以在jQuery或jQuery.fn

四.jQuery源码解析之jQuery.fn.init()的参数解析

从return new jQuery.fn.init( selector, context, rootjQuery )中可以看出 参数selector和context是来自我们在调用jQuery方法时传过来的.那么selector和context都有哪些可能. 对于表格中的4~9行中的可能做具体分析. 如果selector是字符串,则首先检测是html代码还是#id. 126行的if语句:以"<"开头,以">"结尾,且长度>=3.则假设额这个是HT