jQuery源码研究分析学习笔记-静态方法和属性(10)

jQuery源码中定义了一些重要的静态属性和方法,它们是其他模块实现的基础,整体的源码结构如下

    //把window.jQuery和winow.$备份到局部变量_jQuery和_$
    _jQuery = window.jQuery,

    // Map over the $ in case of overwrite
    _$ = window.$,
jQuery.extend({
    //许多 JavaScript 库使用 $ 作为函数或变量名,jQuery 也一样。在 jQuery 中,$ 仅仅是 jQuery 的别名,因此即使不使用 $ 也能保证所有功能性。假如我们需要使用 jQuery 之外的另一 JavaScript 库,我们可以通过调用 $.noConflict() 向该库返回控制权:
    noConflict: function( deep ) {
    //若window.$ ===jQuery,则设置window.$为初始化时备份的_$.
    //只有在当前jQuery库持有全局变量$的情况下,才会释放$的控制器给前一个javascript库
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

        //当deep为true,且window.jQuery===jQuery,则设置window.jQuery为初始化时备份的_jQuery
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }

        return jQuery;
    },

    // Is the DOM ready to be used? Set to true once it occurs.
    isReady: false,

    // A counter to track how many items to wait for before
    // the ready event fires. See #6781
    readyWait: 1,

    // 用于判断传入的参数是否是函数

    isFunction: function( obj ) {
        return jQuery.type(obj) === "function";
    },

    isArray: Array.isArray || function( obj ) {
        return jQuery.type(obj) === "array";
    },

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

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

    //用于判断参数的内建Javascript类型,如果参数是undefined或null,返回"undefined"或"null";如果参数是Js内部对象,则返回对应的字符串名称;其他情况一律返回"object"
    type: function( obj ) {
        return obj == null ?
            String( obj ) :
            //toString = Object.prototype.toString
            //借用Object的原型方法toString()获取obj的字符串表示,返回值的形式为[object class],其中的class是内部对象类,Object.prototype.toString.call(new Date())返回[object Date]
            class2type[ toString.call(obj) ] || "object";
    },
    //用于判断传入的参数是否是”纯粹“的对象,是否是对象直接量{}或 new Object()创建的对象
    isPlainObject: function( obj ) {
        //obj可以转换成false,Object.prototype.toString.call(obj)返回的不是[object object ],obj是DOM元素,obj是window对象,满足一个则返回false
        if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
            return false;
        }

        try {
            // 检查对象obj是否由构造函数Object()创建,
            对象obj含有属性constuctor,由 构造函数创建的对象都有一个constructor属性,默认引用了该对象的构造函数,如果对象obj没有属性constructor,则说明该对象必然是通过对象字面量{} 创建的
            if ( obj.constructor &&
                //hasOwn = Object.prototype.hasOwnProperty
                !hasOwn.call(obj, "constructor") &&
                !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
                return false;
            }
        } catch ( e ) {
            // IE8,9 Will throw exceptions on certain host objects #9897
            return false;
        }

        // 检查对象obj的属性是否都是非继承属性,如果没有属性,或者所有的属性都是非继承,怎返回true

        var key;
        for ( key in obj ) {}

        return key === undefined || hasOwn.call( obj, key );
    },

    //用于检查对象是都是空的(不包含属性)
    isEmptyObject: function( obj ) {

        //for-in循环会同时枚举非继承属性和从原型对象继承的属性,如果有,则返回false,否则默认返回true
        for ( var name in obj ) {
            return false;
        }
        return true;
    },

    //接收一个字符串,抛出一个包含了该字符串的异常
    error: function( msg ) {
        throw new Error( msg );
    },

    //把一个json字符串,解析返回javascript对象,如果传入的残缺的json字符串可能导致抛出异常
    parseJSON: function( data ) {

        //如果不传入参数,或传入空字符串、null、undefined,则返回null
        if ( typeof data !== "string" || !data ) {
            return null;
        }

        // 移除开头和末尾的空白符,兼容IE6/7
        data = jQuery.trim( data );

        // 若支持,使用原生的方法JSON.parse()解析JSON字符串,并返回
        if ( window.JSON && window.JSON.parse ) {

            //JSON.parse()和JSON.stringify(),用于JSON字符串和javaScript对象之间的相互转换
            //JSON.parse()解析JSON字符串为JSON对象,JSON.stringify()转换JSON对象为JSON字符串
            return window.JSON.parse( data );
        }

        // 若不支持JSON.parse(),先检查字符串是否合法,
        // rvalidchars = /^[\],:{}\s]*$/,
        // rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
    // rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
    // rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g

        if ( rvalidchars.test( data.replace( rvalidescape, "@" )
            .replace( rvalidtokens, "]" )
            .replace( rvalidbraces, "")) ) {

            return ( new Function( "return " + data ) )();

        }
        jQuery.error( "Invalid JSON: " + data );
    },

    // 接受一个格式良好的xml字符传,返回解析后的XML文档
    parseXML: function( data ) {
        if ( typeof data !== "string" || !data ) {
            return null;
        }
        var xml, tmp;
        try {
            if ( window.DOMParser ) { 
                //IE9+和其他标准浏览器
                tmp = new DOMParser();
                xml = tmp.parseFromString( data , "text/xml" );
            } else { 

                // IE
                xml = new ActiveXObject( "Microsoft.XMLDOM" );
                xml.async = "false";
                xml.loadXML( data );
            }
        } catch( e ) {
            xml = undefined;
        }
        if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
            jQuery.error( "Invalid XML: " + data );
        }
        return xml;
    },
    //空函数,开发插件的时候,可以作为回调函数的默认值,如果没有提供回调函数,则执行jQuery.noop()
    noop: function() {},

    // 用于在全局作用域中执行javaScript代码

    globalEval: function( data ) {
        if ( data && rnotwhite.test( data ) ) {
            // execScript()在全局作用域中按照指定的脚本语言执行脚本代码,默认语言是Jscirpt,没有返回值
            //如果支持方法execScript(),则执行execScript(data);否则创建一个自调用匿名函数,在函数内部执行
            ( window.execScript || function( data ) {
                window[ "eval" ].call( window, data );
            } )( data );
        }
    },

    // 转换连接字符式的字符串为驼峰式,用于CSS模块和数据缓存模块
    // rdashAlpha = /-([a-z]|[0-9])/ig,
    // rmsPrefix = /^-ms-/,
    camelCase: function( string ) {

        //先用正则rmsPrefix匹配前缀"-ms-",如果有则修正为"ms-",然后用正则rdashAlpha匹配连字符"-"和其后的第一个字母或数字,并用字符串方法replace()和函数fcamelCase()把匹配部分替换为对应的大写字母或数字
        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
    },

    //用于检查DOM元素的节点名称,与指定的值是否相等,检查时忽略大小写
    nodeName: function( elem, name ) {

        //把属性elem.nodeName和参数name转换为大写再做比较,在执行elem.nodeName.toUpperCase()前先检测elem.nodeName是否存在,避免参数elem不是dom元素,或者elem没有属性nodeName导致错误
        return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
    },

    // args is for internal usage only
    each: function( object, callback, args ) {
        //省略...
    },

    // 移除字符串开头和结尾的空白符
    //rnotwhite = /\S/,
    // trimLeft = /^\s+/,
    // trimRight = /\s+$/,
    // trim = String.prototype.trim
    trim: trim ?
        function( text ) {

            //如果传入的参数是null或undefined,则返回空字符串
            //支持String.prototype.trim()则借用此方法
            return text == null ?
                "" :
                trim.call( text );
        } :

        // 不支持,则先调用方法toString()得到参数text的字符串表示,然后用正则trimLeft和trimRight匹配到的空白符替换为空字符串
        function( text ) {
            return text == null ?
                "" :
                text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
        },

    // 将一个类数组对象转换成真正的数组
    // array:待转换对象,可以是任何类型
    //results:仅在jQuery内部使用,如果传入参数results,则在该参数上添加元素
    makeArray: function( array, results ) {
        var ret = results || []; //定义返回值

        if ( array != null ) {


            var type = jQuery.type( array );

            if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
            //array没有属性,array是字符串,array是函数,是window对象,是正则对象,满足一个则断定参数array不是数组
            //插入元素时执行的是push.call(ret,array),这是因为返回值ret不一定是真正的数组。如果只传入参数array,则返回值ret是真正的数组,如果还传入的第二个参数ret,则返回值ret的类型取决于该参数的类型
                push.call( ret, array );
            } else {
    //参数array是数组或类数组对象,调用jQuery.merge()把该参数合并到返回的值ret中
                jQuery.merge( ret, array );
            }
        }

        return ret;
    },

    //在数组中查找指定元素的并返回其下标
    // elem: 要查找的值
    //array:数组,将被遍历的数组,来查找参数value在其中的下标
    //i: 指定开始查找的位置,默认是0
    inArray: function( elem, array, i ) {
        var len;

        if ( array ) {
        //如果支持数组方法indexOf(),则调用它并返回下标
            if ( indexOf ) {
                return indexOf.call( array, elem, i );
            }

            len = array.length;

            //修正参数i,若没指定参数i,则初始化为0,如果小于0,则加上数组长度,即从数组末尾开始计算,Math.max()方法在0与len+i之间取最大值,如果len+i仍然小于0,则把i修正为0
            i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;

            for ( ; i < len; i++ ) {
                // Skip accessing in sparse arrays
                if ( i in array && array[ i ] === elem ) {
                    return i;
                }
            }
        }

        return -1;
    },

    //合并两个数组到第一个数组,第一个数组将会被改变
    //first: 数组或类数组对象,必须含有整型属性length
    //second: 数组、类数组对象或任何含有连续整型属性的对象,其中的元素会被合并到第一个参数first中
    merge: function( first, second ) {
        var i = first.length,
            j = 0;

        //判断second的length是否是数值类型
        if ( typeof second.length === "number" ) {
            for ( var l = second.length; j < l; j++ ) {
                first[ i++ ] = second[ j ];
            }

        } else {

            //second没有属性length,则把该参数当作含有连续整型属性的对象,把其中的非undefined元素逐个插入参数first中
            while ( second[j] !== undefined ) {
                first[ i++ ] = second[ j++ ];
            }
        }

        //手动修正first的length
        first.length = i;

        return first; //返回修改后的first数组
    },

    //用于查找数组中满足过滤函数的元素,原数组不会受影响
    //array:待遍历的数组
    //callback: 过滤每个元素的函数,执行时被传入两个参数:当前元素和它的下标,返回一个布尔值
    grep: function( elems, callback, inv ) {
        var ret = [], retVal;
        inv = !!inv;

        // 遍历数组,为每个元素执行过滤函数,如果inv为true,把执行结果false的元素放入结果数组ret中,
        // that pass the validator function
        for ( var i = 0, length = elems.length; i < length; i++ ) {
            retVal = !!callback( elems[ i ], i );
            if ( inv !== retVal ) {
                ret.push( elems[ i ] );
            }
        }

        return ret;
    },

    // arg is for internal usage only
    map: function( elems, callback, arg ) {
        //省略
    },

    // 全局计数器,用于jQuery事件模块和缓存模块,
    guid: 1,

    //接受一个函数返回一个新的函数,新函数总是持有特定的上下文
    proxy: function( fn, context ) {

        //如果参数context是字符串,说明参数格式是jQuery.proxy(context,name),修正为jQuery.proxy(fn,context)
        if ( typeof context === "string" ) {
            var tmp = fn[ context ];
            context = fn;
            fn = tmp;
        }

        // 如果fn不是函数,则返回undefined
        if ( !jQuery.isFunction( fn ) ) {
            return undefined;
        }

        // 收集多余参数,借用数组的slice()方法
        var args = slice.call( arguments, 2 ),
            proxy = function() {
                return fn.apply( context, args.concat( slice.call( arguments ) ) );
            };

        // 为代理函数设置与原始函数相同的唯一标识guid,如果原始函数没有,则重新分配一个
        proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;

        //返回创建的代码函数
        return proxy;
    },


    //获取当前时间
    now: function() {
        return ( new Date() ).getTime();
    },

    // 浏览器嗅探

    uaMatch: function( ua ) {
        ua = ua.toLowerCase();

        var match = rwebkit.exec( ua ) ||
            ropera.exec( ua ) ||
            rmsie.exec( ua ) ||
            ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
            [];

        return { browser: match[1] || "", version: match[2] || "0" };
    },

    sub: function() {
        function jQuerySub( selector, context ) {
            return new jQuerySub.fn.init( selector, context );
        }
        jQuery.extend( true, jQuerySub, this );
        jQuerySub.superclass = this;
        jQuerySub.fn = jQuerySub.prototype = this();
        jQuerySub.fn.constructor = jQuerySub;
        jQuerySub.sub = this.sub;
        jQuerySub.fn.init = function init( selector, context ) {
            if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
                context = jQuerySub( context );
            }

            return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
        };
        jQuerySub.fn.init.prototype = jQuerySub.fn;
        var rootjQuerySub = jQuerySub(document);
        return jQuerySub;
    },

    browser: {}
})
// 对象class2type
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

函数fcamelCase ()负责把连字符后的字母转换成为大写并返回

fcamelCase = function( all, letter ) {
        return ( letter + "" ).toUpperCase();
    }

你可能感兴趣的:(jQuery,源码研究)