深入学习jquery源码之merge()和unique()

时间:2023-02-23 21:58:22


深入学习jquery源码之merge()

概述:

合并两个数组到第一个数组上。

返回的结果会修改第一个数组的内容——第一个数组的元素后面跟着第二个数组的元素。要去除重复项,请使用$.unique()

参数:Array,Array

first,second

first:第一个待处理数组,会改变其中的元素。

second:第二个待处理数组,不会改变其中的元素。

使用:

$.merge( [0,1,2], [2,3,4] )
[0,1,2,2,3,4]

 

jquery源码

(function (global, factory) {

if (typeof module === "object" && typeof module.exports === "object") {
module.exports = global.document ?
factory(global, true) :
function (w) {
if (!w.document) {
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
} else {
factory(global);
}

}(typeof window !== "undefined" ? window : this, function (window, noGlobal) {

var
version = "1.11.3",

// Define a local copy of jQuery
jQuery = function (selector, context) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init(selector, context);
}
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version,

constructor: jQuery,

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

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

toArray: function () {
return slice.call(this);
}
push: push,
sort: deletedIds.sort,
splice: deletedIds.splice
};

jQuery.extend = jQuery.fn.extend = function () {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;

// Handle a deep copy situation
if (typeof target === "boolean") {
deep = target;

// skip the boolean and the target
target = arguments[i] || {};
i++;
}

// 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 (i === length) {
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({

merge: function (first, second) {
var len = +second.length,
j = 0,
i = first.length;

while (j < len) {
first[i++] = second[j++];
}

// Support: IE<9
// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
if (len !== len) {
while (second[j] !== undefined) {
first[i++] = second[j++];
}
}

first.length = i;

return first;
}
});


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

// Use the correct document accordingly with window argument (sandbox)
document = window.document,

// A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,

init = jQuery.fn.init = function (selector, context) {
var match, elem;

// HANDLE: $(""), $(null), $(undefined), $(false)
if (!selector) {
return this;
}

// Handle HTML strings
if (typeof selector === "string") {
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 = rquickExpr.exec(selector);
}

// Match html or make sure no context is specified for #id
if (match && (match[1] || !context)) {

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

// scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present
jQuery.merge(this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
));

// HANDLE: $(html, props)
if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
for (match in context) {
// Properties of context are called as methods if possible
if (jQuery.isFunction(this[match])) {
this[match](context[match]);

// ...and otherwise set as attributes
} else {
this.attr(match, context[match]);
}
}
}

return this;

// 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: $(DOMElement)
} else if (selector.nodeType) {
this.context = this[0] = selector;
this.length = 1;
return this;

// HANDLE: $(function)
// Shortcut for document ready
} else if (jQuery.isFunction(selector)) {
return typeof rootjQuery.ready !== "undefined" ?
rootjQuery.ready(selector) :
// Execute immediately if ready is not present
selector(jQuery);
}

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

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

// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

// Initialize central reference
rootjQuery = jQuery(document);


jQuery.noConflict = function (deep) {
if (window.$ === jQuery) {
window.$ = _$;
}

if (deep && window.jQuery === jQuery) {
window.jQuery = _jQuery;
}

return jQuery;
};

if (typeof noGlobal === strundefined) {
window.jQuery = window.$ = jQuery;
}
return jQuery;

}));

 

jQuery.unique(array)

概述

删除数组中重复元素。只处理删除DOM元素数组,而不能处理字符串或者数字数组。

参数

array Array

待处理数组。

删除重复 div 标签。

$.unique(document.getElementsByTagName("div"));
[<div>, <div>, ...]

 

jquery源码

var Sizzle =
(function (window) {

jQuery.unique = Sizzle.uniqueSort;


function Sizzle(selector, context, results, seed) {
var match, elem, m, nodeType,
// QSA vars
i, groups, old, nid, newContext, newSelector;

if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
setDocument(context);
}

context = context || document;
results = results || [];
nodeType = context.nodeType;

if (typeof selector !== "string" || !selector ||
nodeType !== 1 && nodeType !== 9 && nodeType !== 11) {

return results;
}

if (!seed && documentIsHTML) {

// Try to shortcut find operations when possible (e.g., not under DocumentFragment)
if (nodeType !== 11 && (match = rquickExpr.exec(selector))) {
// Speed-up: Sizzle("#ID")
if ((m = match[1])) {
if (nodeType === 9) {
elem = context.getElementById(m);
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document (jQuery #6963)
if (elem && elem.parentNode) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if (elem.id === m) {
results.push(elem);
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) &&
contains(context, elem) && elem.id === m) {
results.push(elem);
return results;
}
}

// Speed-up: Sizzle("TAG")
} else if (match[2]) {
push.apply(results, context.getElementsByTagName(selector));
return results;

// Speed-up: Sizzle(".CLASS")
} else if ((m = match[3]) && support.getElementsByClassName) {
push.apply(results, context.getElementsByClassName(m));
return results;
}
}

// QSA path
if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
nid = old = expando;
newContext = context;
newSelector = nodeType !== 1 && selector;

// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") {
groups = tokenize(selector);

if ((old = context.getAttribute("id"))) {
nid = old.replace(rescape, "\\$&");
} else {
context.setAttribute("id", nid);
}
nid = "[id='" + nid + "'] ";

i = groups.length;
while (i--) {
groups[i] = nid + toSelector(groups[i]);
}
newContext = rsibling.test(selector) && testContext(context.parentNode) || context;
newSelector = groups.join(",");
}

if (newSelector) {
try {
push.apply(results,
newContext.querySelectorAll(newSelector)
);
return results;
} catch (qsaError) {
} finally {
if (!old) {
context.removeAttribute("id");
}
}
}
}
}

// All others
return select(selector.replace(rtrim, "$1"), context, results, seed);
}


/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function (results) {
var elem,
duplicates = [],
j = 0,
i = 0;

// Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice(0);
results.sort(sortOrder);

if (hasDuplicate) {
while ((elem = results[i++])) {
if (elem === results[i]) {
j = duplicates.push(i);
}
}
while (j--) {
results.splice(duplicates[j], 1);
}
}

// Clear input after sorting to release objects
// See https://github.com/jquery/sizzle/pull/225
sortInput = null;

return results;
};

return Sizzle;

})(window);