jquery原来是这么回事

时间:2021-11-06 20:09:50

JQuery总的来讲是一个包含若干属性的对象,通过extend进行扩展

var jQuery = (function() {

// Define a local copy of jQuery
var jQuery = function(selector, context) {
return new jQuery.fn.init(selector, context, rootjQuery);//init方法是具有参数的
},

// Map over jQuery in case of overwrite
_jQuery = window.jQuery,

// Map over the $ in case of overwrite
_$ = window.$,

// A central reference to the root jQuery(document)
rootjQuery,

// A simple way to check for HTML strings or ID strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,

// Check if a string has a non-whitespace character in it
rnotwhite = /\S/,

// Used for trimming whitespace
trimLeft = /^\s+/, trimRight = /\s+$/,

// Match a standalone tag
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,

// JSON RegExp
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,

// Useragent RegExp
rwebkit = /(webkit)[ \/]([\w.]+)/, ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, rmsie = /(msie) ([\w.]+)/, rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,

// Matches dashed string for camelizing
rdashAlpha = /-([a-z]|[0-9])/ig, rmsPrefix = /^-ms-/,

// Used by jQuery.camelCase as callback to replace()
fcamelCase = function(all, letter) {
return (letter + "").toUpperCase();
},

// Keep a UserAgent string for use with jQuery.browser
userAgent = navigator.userAgent,

// For matching the engine and version of the browser
browserMatch,

// The deferred used on DOM ready
readyList,

// The ready event handler
DOMContentLoaded,

// Save a reference to some core methods
toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, push = Array.prototype.push, slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf,

// [[Class]] -> type pairs
class2type = {};

//这才是最原始的jquery对象
jQuery.fn = jQuery.prototype = {
constructor : jQuery,

init : function(selector, context, rootjQuery) {
var match, elem, ret, doc;

// Handle $(""), $(null), or $(undefined)
if (!selector) {
return this;
}

// Handle $(DOMElement)
if (selector.nodeType) {
this.context = this[0] = selector;
this.length = 1;
return this;
}

// The body element only exists once, optimize finding it
if (selector === "body" && !context && document.body) {
this.context = document;
this[0] = document.body;
this.selector = selector;
this.length = 1;
return this;
}

// Handle HTML strings
if (typeof selector === "string") {
// Are we dealing with HTML string or an ID?
if (selector.charAt(0) === "<"
&& selector.charAt(selector.length - 1) === ">"
&& selector.length >= 3) {
// Assume that strings that start and end with <> are
// HTML and skip the regex check
match = [ null, selector, null ];

} else {
match = quickExpr.exec(selector);
}

// Verify a match, and that no context was specified for #id
if (match && (match[1] || !context)) {

// HANDLE: $(html) -> $(array)
if (match[1]) {
context = context instanceof jQuery ? context[0]
: context;
doc = (context ? context.ownerDocument || context
: document);

// If a single string is passed in and it's a single
// tag
// just do a createElement and skip the rest
ret = rsingleTag.exec(selector);

if (ret) {
if (jQuery.isPlainObject(context)) {
selector = [ document.createElement(ret[1]) ];
jQuery.fn.attr.call(selector, context, true);

} else {
selector = [ doc.createElement(ret[1]) ];
}

} else {
ret = jQuery.buildFragment([ match[1] ], [ doc ]);
selector = (ret.cacheable ? jQuery
.clone(ret.fragment) : ret.fragment).childNodes;
}

return jQuery.merge(this, selector);

// HANDLE: $("#id")
} else {
elem = document.getElementById(match[2]);

// Check parentNode to catch when Blackberry 4.6
// returns
// nodes that are no longer in the document #6963
if (elem && elem.parentNode) {
// Handle the case where IE and Opera return
// items
// by name instead of ID
if (elem.id !== match[2]) {
return rootjQuery.find(selector);
}

// Otherwise, we inject the element directly
// into the jQuery object
this.length = 1;
this[0] = elem;
}

this.context = document;
this.selector = selector;
return this;
}

// HANDLE: $(expr, $(...))
} else if (!context || context.jquery) {
return (context || rootjQuery).find(selector);

// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor(context).find(selector);
}

// HANDLE: $(function)
// Shortcut for document ready
} else if (jQuery.isFunction(selector)) {
return rootjQuery.ready(selector);
}

if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}

return jQuery.makeArray(selector, this);
},

// Start with an empty selector
selector : "",

// The current version of jQuery being used
jquery : "1.7.2",

// The default length of a jQuery object is 0
length : 0,

// The number of elements contained in the matched element set
size : function() {
return this.length;
},

toArray : function() {
return slice.call(this, 0);
},

// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get : function(num) {
return num == null ?

// Return a 'clean' array
this.toArray() :

// Return just the object
(num < 0 ? this[this.length + num] : this[num]);
},

// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack : function(elems, name, selector) {
// Build a new jQuery matched element set
var ret = this.constructor();

if (jQuery.isArray(elems)) {
push.apply(ret, elems);

} else {
jQuery.merge(ret, elems);
}

// Add the old object onto the stack (as a reference)
ret.prevObject = this;

ret.context = this.context;

if (name === "find") {
ret.selector = this.selector + (this.selector ? " " : "")
+ selector;
} else if (name) {
ret.selector = this.selector + "." + name + "(" + selector
+ ")";
}

// Return the newly-formed element set
return ret;
},

// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each : function(callback, args) {
return jQuery.each(this, callback, args);
},

ready : function(fn) {
// Attach the listeners
jQuery.bindReady();

// Add the callback
readyList.add(fn);

return this;
},

eq : function(i) {
i = +i;
return i === -1 ? this.slice(i) : this.slice(i, i + 1);
},

first : function() {
return this.eq(0);
},

last : function() {
return this.eq(-1);
},

slice : function() {
return this.pushStack(slice.apply(this, arguments), "slice", slice
.call(arguments).join(","));
},

map : function(callback) {
return this.pushStack(jQuery.map(this, function(elem, i) {
return callback.call(elem, i, elem);
}));
},

end : function() {
return this.prevObject || this.constructor(null);
},

// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push : push,
sort : [].sort,
splice : [].splice
};
//此处向上是最原始的JQuery

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
//使用jquery的原型来代替init.prototype,因为init.prototype只是生成了一个object

//接下来是object的扩展方法
//注意:arguments参数看成是一个动态参数,在函数的定义过程中可以不声明形参,在函数中通过arguments[X]获取
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

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 : {}
});

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

browserMatch = jQuery.uaMatch(userAgent);
if (browserMatch.browser) {
jQuery.browser[browserMatch.browser] = true;
jQuery.browser.version = browserMatch.version;
}

// Deprecated, use jQuery.browser.webkit instead
if (jQuery.browser.webkit) {
jQuery.browser.safari = true;
}

// IE doesn't match non-breaking spaces with \s
if (rnotwhite.test("\xA0")) {
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
}

// All jQuery objects should point back to these
rootjQuery = jQuery(document);

// Cleanup functions for the document ready method
if (document.addEventListener) {
DOMContentLoaded = function() {
document.removeEventListener("DOMContentLoaded", DOMContentLoaded,
false);
jQuery.ready();
};

} else if (document.attachEvent) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little
// overzealous (ticket #5443).
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", DOMContentLoaded);
jQuery.ready();
}
};
}

// The DOM ready check for Internet Explorer
function doScrollCheck() {
if (jQuery.isReady) {
return;
}

try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch (e) {
setTimeout(doScrollCheck, 1);
return;
}

// and execute any waiting functions
jQuery.ready();
}

return jQuery;

})();