从jQuery中抽取工具函数

每一个JS框架中都首先包含一些工具函数,为框架代码打下基础:

jQuery工具函数挺多的,下面列出主要部分,其实有很多是可以在项目中用到的,标注* 号的暂未支持:

noConflict
ready(* )(抽取了Callback出来后,这里需要进行相应的调整)
isFunction
isArray
isWindow
isNumeric(这个非常有意思,判断的比较全面!isNaN( parseFloat(obj) ) && isFinite( obj );)
type
isPlainObject(测试对象是否是纯粹的对象(通过 "{}" 或者 "new Object" 创建的)。可以用于区别new Object() new Array()等对象,它们的typeof 也是Object)
isEmptyObject
error(抛出一个异常信息)
parseJSON
parseXML
noop(空操作)
nodeName
each
trim


// 数组的操作
makeArray
inArray
merge
grep
map

//
proxy
access

// 当前时间获取函数,可以有效减少代码量
now()


// jQuery.extend方法定义了一个向fn扩展属性及方法的接口,后续jQuery对象的扩展都基于此函数  
jQuery.extend = jQuery.fn.extend = function() {  
    var options, name, src, copy, copyIsArray, clone,  
        target = arguments[0] || {},  
        i = 1,  
        length = arguments.length,  
        deep = false;  
  
    // Handle a deep copy situation  
    if ( typeof target === "boolean" ) {  
        deep = target;  
        target = arguments[1] || {};  
        // skip the boolean and the target  
        i = 2;  
    }  
  
    // Handle case when target is a string or something (possible in deep copy)  
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {  
        target = {};  
    }  
  
    // extend jQuery itself if only one argument is passed  
    if ( length === i ) {  
        target = this;  
        --i;  
    }  
  
    for ( ; i < length; i++ ) {  
        // Only deal with non-null/undefined values  
        if ( (options = arguments[ i ]) != null ) {  
            // Extend the base object  
            for ( name in options ) {  
                src = target[ name ];  
                copy = options[ name ];  
  
                // Prevent never-ending loop  
                if ( target === copy ) {  
                    continue;  
                }  
  
                // Recurse if we're merging plain objects or arrays  
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {  
                    if ( copyIsArray ) {  
                        copyIsArray = false;  
                        clone = src && jQuery.isArray(src) ? src : [];  
  
                    } else {  
                        clone = src && jQuery.isPlainObject(src) ? src : {};  
                    }  
  
                    // Never move original objects, clone them  
                    target[ name ] = jQuery.extend( deep, clone, copy );  
  
                // Don't bring in undefined values  
                } else if ( copy !== undefined ) {  
                    target[ name ] = copy;  
                }  
            }  
        }  
    }  
  
    // Return the modified object  
    return target;  
};  
  
jQuery.extend({  
    noConflict: function( deep ) {  
        if ( window.$ === jQuery ) {  
            window.$ = _$;  
        }  
  
        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,  
  
    // Hold (or release) the ready event  
    holdReady: function( hold ) {  
        if ( hold ) {  
            jQuery.readyWait++;  
        } else {  
            jQuery.ready( true );  
        }  
    },  
  
    // Handle when the DOM is ready  
    ready: function( wait ) {  
        // Either a released hold or an DOMready/load event and not yet ready  
        if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {  
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).  
            if ( !document.body ) {  
                return setTimeout( jQuery.ready, 1 );  
            }  
  
            // Remember that the DOM is ready  
            jQuery.isReady = true;  
  
            // If a normal DOM Ready event fired, decrement, and wait if need be  
            if ( wait !== true && --jQuery.readyWait > 0 ) {  
                return;  
            }  
  
            // If there are functions bound, to execute  
            readyList.fireWith( document, [ jQuery ] );  
  
            // Trigger any bound ready events  
            if ( jQuery.fn.trigger ) {  
                jQuery( document ).trigger( "ready" ).off( "ready" );  
            }  
        }  
    },  
  
    bindReady: function() {  
        if ( readyList ) {  
            return;  
        }  
  
        readyList = jQuery.Callbacks( "once memory" );  
  
        // Catch cases where $(document).ready() is called after the  
        // browser event has already occurred.  
        if ( document.readyState === "complete" ) {  
            // Handle it asynchronously to allow scripts the opportunity to delay ready  
            return setTimeout( jQuery.ready, 1 );  
        }  
  
        // Mozilla, Opera and webkit nightlies currently support this event  
        if ( document.addEventListener ) {  
            // Use the handy event callback  
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );  
  
            // A fallback to window.onload, that will always work  
            window.addEventListener( "load", jQuery.ready, false );  
  
        // If IE event model is used  
        } else if ( document.attachEvent ) {  
            // ensure firing before onload,  
            // maybe late but safe also for iframes  
            document.attachEvent( "onreadystatechange", DOMContentLoaded );  
  
            // A fallback to window.onload, that will always work  
            window.attachEvent( "onload", jQuery.ready );  
  
            // If IE and not a frame  
            // continually check to see if the document is ready  
            var toplevel = false;  
  
            try {  
                toplevel = window.frameElement == null;  
            } catch(e) {}  
  
            if ( document.documentElement.doScroll && toplevel ) {  
                doScrollCheck();  
            }  
        }  
    },  
  
    // 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";  
    },  
  
    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 );  
    },  
  
    type: function( obj ) {  
        return obj == null ?  
            String( obj ) :  
            class2type[ toString.call(obj) ] || "object";  
    },  
  
    isPlainObject: function( obj ) {  
        // Must be an Object.  
        // Because of IE, we also have to check the presence of the constructor property.  
        // Make sure that DOM nodes and window objects don't pass through, as well  
        if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {  
            return false;  
        }  
  
        try {  
            // Not own constructor property must be Object  
            if ( obj.constructor &&  
                !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;  
        }  
  
        // Own properties are enumerated firstly, so to speed up,  
        // if last one is own, then all properties are own.  
  
        var key;  
        for ( key in obj ) {}  
  
        return key === undefined || hasOwn.call( obj, key );  
    },  
  
    isEmptyObject: function( obj ) {  
        for ( var name in obj ) {  
            return false;  
        }  
        return true;  
    },  
  
    error: function( msg ) {  
        throw new Error( msg );  
    },  
  
    parseJSON: function( data ) {  
        if ( typeof data !== "string" || !data ) {  
            return null;  
        }  
  
        // Make sure leading/trailing whitespace is removed (IE can't handle it)  
        data = jQuery.trim( data );  
  
        // Attempt to parse using the native JSON parser first  
        if ( window.JSON && window.JSON.parse ) {  
            return window.JSON.parse( data );  
        }  
  
        // Make sure the incoming data is actual JSON  
        // Logic borrowed from http://json.org/json2.js  
        if ( rvalidchars.test( data.replace( rvalidescape, "@" )  
            .replace( rvalidtokens, "]" )  
            .replace( rvalidbraces, "")) ) {  
  
            return ( new Function( "return " + data ) )();  
  
        }  
        jQuery.error( "Invalid JSON: " + data );  
    },  
  
    // Cross-browser xml parsing  
    parseXML: function( data ) {  
        if ( typeof data !== "string" || !data ) {  
            return null;  
        }  
        var xml, tmp;  
        try {  
            if ( window.DOMParser ) { // Standard  
                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;  
    },  
  
    noop: function() {},  
  
    // Evaluates a script in a global context  
    // Workarounds based on findings by Jim Driscoll  
    // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context  
    globalEval: function( data ) {  
        if ( data && rnotwhite.test( data ) ) {  
            // We use execScript on Internet Explorer  
            // We use an anonymous function so that context is window  
            // rather than jQuery in Firefox  
            ( window.execScript || function( data ) {  
                window[ "eval" ].call( window, data );  
            } )( data );  
        }  
    },  
  
    // Convert dashed to camelCase; used by the css and data modules  
    // Microsoft forgot to hump their vendor prefix (#9572)  
    camelCase: function( string ) {  
        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );  
    },  
  
    nodeName: function( elem, name ) {  
        return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();  
    },  
  
    // args is for internal usage only  
    each: function( object, callback, args ) {  
        var name, i = 0,  
            length = object.length,  
            isObj = length === undefined || jQuery.isFunction( object );  
  
        if ( args ) {  
            if ( isObj ) {  
                for ( name in object ) {  
                    if ( callback.apply( object[ name ], args ) === false ) {  
                        break;  
                    }  
                }  
            } else {  
                for ( ; i < length; ) {  
                    if ( callback.apply( object[ i++ ], args ) === false ) {  
                        break;  
                    }  
                }  
            }  
  
        // A special, fast, case for the most common use of each  
        } else {  
            if ( isObj ) {  
                for ( name in object ) {  
                    if ( callback.call( object[ name ], name, object[ name ] ) === false ) {  
                        break;  
                    }  
                }  
            } else {  
                for ( ; i < length; ) {  
                    if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {  
                        break;  
                    }  
                }  
            }  
        }  
  
        return object;  
    },  
  
    // Use native String.trim function wherever possible  
    trim: trim ?  
        function( text ) {  
            return text == null ?  
                "" :  
                trim.call( text );  
        } :  
  
        // Otherwise use our own trimming functionality  
        function( text ) {  
            return text == null ?  
                "" :  
                text.toString().replace( trimLeft, "" ).replace( trimRight, "" );  
        },  
  
    // results is for internal usage only  
    makeArray: function( array, results ) {  
        var ret = results || [];  
  
        if ( array != null ) {  
            // The window, strings (and functions) also have 'length'  
            // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930  
            var type = jQuery.type( array );  
  
            if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {  
                push.call( ret, array );  
            } else {  
                jQuery.merge( ret, array );  
            }  
        }  
  
        return ret;  
    },  
  
    inArray: function( elem, array, i ) {  
        var len;  
  
        if ( array ) {  
            if ( indexOf ) {  
                return indexOf.call( array, elem, i );  
            }  
  
            len = array.length;  
            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;  
    },  
  
    merge: function( first, second ) {  
        var i = first.length,  
            j = 0;  
  
        if ( typeof second.length === "number" ) {  
            for ( var l = second.length; j < l; j++ ) {  
                first[ i++ ] = second[ j ];  
            }  
  
        } else {  
            while ( second[j] !== undefined ) {  
                first[ i++ ] = second[ j++ ];  
            }  
        }  
  
        first.length = i;  
  
        return first;  
    },  
  
    grep: function( elems, callback, inv ) {  
        var ret = [], retVal;  
        inv = !!inv;  
  
        // Go through the array, only saving the items  
        // 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 ) {  
        var value, key, ret = [],  
            i = 0,  
            length = elems.length,  
            // jquery objects are treated as arrays  
            isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;  
  
        // Go through the array, translating each of the items to their  
        if ( isArray ) {  
            for ( ; i < length; i++ ) {  
                value = callback( elems[ i ], i, arg );  
  
                if ( value != null ) {  
                    ret[ ret.length ] = value;  
                }  
            }  
  
        // Go through every key on the object,  
        } else {  
            for ( key in elems ) {  
                value = callback( elems[ key ], key, arg );  
  
                if ( value != null ) {  
                    ret[ ret.length ] = value;  
                }  
            }  
        }  
  
        // Flatten any nested arrays  
        return ret.concat.apply( [], ret );  
    },  
  
    // A global GUID counter for objects  
    guid: 1,  
  
    // Bind a function to a context, optionally partially applying any  
    // arguments.  
    proxy: function( fn, context ) {  
        if ( typeof context === "string" ) {  
            var 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  
        var args = slice.call( arguments, 2 ),  
            proxy = function() {  
                return fn.apply( context, args.concat( 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 || proxy.guid || jQuery.guid++;  
  
        return proxy;  
    },  
  
    // Mutifunctional method to get and set values to a collection  
    // The value/s can optionally be executed if it's a function  
    access: function( elems, fn, key, value, chainable, emptyGet, pass ) {  
        var exec,  
            bulk = key == null,  
            i = 0,  
            length = elems.length;  
  
        // Sets many values  
        if ( key && typeof key === "object" ) {  
            for ( i in key ) {  
                jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );  
            }  
            chainable = 1;  
  
        // Sets one value  
        } else if ( value !== undefined ) {  
            // Optionally, function values get executed if exec is true  
            exec = pass === undefined && jQuery.isFunction( value );  
  
            if ( bulk ) {  
                // Bulk operations only iterate when executing function values  
                if ( exec ) {  
                    exec = fn;  
                    fn = function( elem, key, value ) {  
                        return exec.call( jQuery( elem ), value );  
                    };  
  
                // Otherwise they run against the entire set  
                } else {  
                    fn.call( elems, value );  
                    fn = null;  
                }  
            }  
  
            if ( fn ) {  
                for (; i < length; i++ ) {  
                    fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );  
                }  
            }  
  
            chainable = 1;  
        }  
  
        return chainable ?  
            elems :  
  
            // Gets  
            bulk ?  
                fn.call( elems ) :  
                length ? fn( elems[0], key ) : emptyGet;  
    },  
  
    now: function() {  
        return ( new Date() ).getTime();  
    },  
  
    // Use of jQuery.browser is frowned upon.  
    // More details: http://docs.jquery.com/Utilities/jQuery.browser  
    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: {}  
});  

你可能感兴趣的:(jquery)