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

这篇对下面的方法进行讲解:

  isFunction():是否为函数

  isArray():是否为数组

  isWindow():是否为window

  isNumeric()是否为数字

  type():判断数据类型

  isPlainObject():是否为对象自变量

  isEmptyObject():是否为空的对象

isFunction方法:

这个方法很简单,判断对象是否为函数,返回bool类型,看一下源码:

// See test/unit/core.js for details concerning isFunction.
    // Since version 1.3, DOM methods and functions like alert
    // aren't supported. They return false on IE (#2968).
    isFunction: function( obj ) {
        return jQuery.type(obj) === "function";
    },

源码很简单,通过调用jQuery.type方法判断返回值是否为function,这里需要注意的是注释部分,注释上说在IE老的版本浏览器中,从1.3以后就对DOM的方法和原生的方法不在支持了,这时返回的是false。

isArray方法:

isArray: Array.isArray,

可以看到这里直接用原生数组的方法,这也是因为原生的方法速度会比较快,所以有原生方法还是最后用原生的方法。

isWindow方法:

isWindow: function( obj ) {
        return obj != null && obj === obj.window;
    },

这个方法用来判断是否为window对象,这里用到了两个条件,先来看第一个:

obj != null,和null进行比较,只有null和undefined比较才会为真,所以这里先对这两种情况进行判断,以免后面的条件报错。

obj === obj.window 如果是window对象的话,其实下面的属性还会有个window属性,并且可以一直无限的调用例如: window.window.window.window..

这里先不说具体原因,等到以后在进行说明。

isNumeric方法:

isNumeric: function( obj ) {
        return !isNaN( parseFloat(obj) ) && isFinite( obj );
    },

这个方法用来判断是否为number类型,那么这里为什么这么麻烦,而不是直接用typeof方法判断呢,是因为:

 console.log(typeof NaN); //number

可以看到,用typeof方法判断NaN的时候返回也是number,所以为了可靠,不能使用原生的方法。

第一个条件中,先对参数进行转换,如果可以的话,那也就是为number类型,如果转换不了,返回的是NaN,也就不满足条件了。

第二个条件用了一个isFinite方法,这个方法是用来判断数字是否在可用范围内,因为计算机计算的数据有限,有时可能传入一个很大的数字,超出了计算机的运算范围,这里也返回false。

console.log(isFinite(Number.MAX_VALUE+Number.MAX_VALUE)); //false

type方法:

type方法返回的是传入参数的数据类型,如:

  function a(){};
  console.log($.type(a)); //function

源码:

type: function( obj ) {
        if ( obj == null ) {
            return String( obj );
        }
        // Support: Safari <= 5.1 (functionish RegExp)
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[ core_toString.call(obj) ] || "object" :
            typeof obj;
    },

首先对传入的参数进行判断,如果为null或undefined,直接返回这两种类型的字符串。

接下来判断参数是否为object或者function,如果是,则运行class2type[ core_toString.call(obj) ] || "object" ,否则直接返回 typeof obj就可以了,因为这种情况只能是基本类型,用typeof判断就足够了。

下面详细介绍这里最重要的一句:

class2type[ core_toString.call(obj) ] || "object"

core_toString对象其实就是{}.toString方法。在源码56行:

core_toString = class2type.toString,

可以找到,下面介绍一下这个tostring方法:

  var arr=new Array();
  console.log({}.toString.call(arr)); //[object Array]

这个方法还可以对date、function等进行判断,我们可以知道,如果用原生的方法,例如date类型,那返回的是object,如:

var arr=new Array();
console.log(typeof arr); //object

所以最好使用这种情况进行类型的判断,这种判断是相当准确的。

下面在看一下class2type这个对象中保存了什么:

// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

可以看到,这个方法把所有类型都放到了这个对象里,然后拼好 [object +name] 对应的属性名和简写的值,那以后直接就可以通过属性名找出对应简写的数据类型了。

isPlainObject方法:

这个方法是判断是否为对象自变量的。返回bool值,源码:

isPlainObject: function( obj ) {
        // Not plain objects:
        // - Any object or value whose internal [[Class]] property is not "[object Object]"
        // - DOM nodes
        // - window
        if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
            return false;
        }

        // Support: Firefox <20
        // The try/catch suppresses exceptions thrown when attempting to access
        // the "constructor" property of certain host objects, ie. |window.location|
        // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
        try {
            if ( obj.constructor &&
                    !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
                return false;
            }
        } catch ( e ) {
            return false;
        }

        // If the function hasn't returned already, we're confident that
        // |obj| is a plain object, created by {} or constructed with new Object
        return true;
    },

第一个条件:

if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
            return false;
        }

这里先进行判断,如果不是obj类型,或者是节点类型,也或者是window对象,那么直接返回false。

// Support: Firefox <20
        // The try/catch suppresses exceptions thrown when attempting to access
        // the "constructor" property of certain host objects, ie. |window.location|
        // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
        try {
            if ( obj.constructor &&
                    !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
                return false;
            }
        } catch ( e ) {
            return false;
        }

这里对另一种情况进行判断,有一种特殊的情况就是传入window.location的时候,因为上面的判断语句无法对这种情况进行判断。

首先判断这个对象是否有constructor,如果有,在判断其原型上是否有isPrototypeOf方法,如果没有,那么则返回false,core_hasOwn:

core_hasOwn = class2type.hasOwnProperty,

从这里可以看到这里之前调用object的hasOwnProperty方法。

这段代码为什么要用try呢,注释中的说明是因为在火狐20一下的版本中,如果多次调用window.location.contructor时,可能会出现too much recursion 这个错误,具体原因请看:https://bugzilla.mozilla.org/show_bug.cgi?id=814622

isEmptyObject方法:

这个方法用来判断是否为空对象。

isEmptyObject: function( obj ) {
        var name;
        for ( name in obj ) {
            return false;
        }
        return true;
    },

这里看如直接用for in 方法来处理,如果对象内有属性,则会被 for in到,返回false。另外for in方法只会对自己添加属性进行遍历,而不会对自带属性遍历。