jQuery创建实例对象及其原型上的方法扩展
jQuery创建实例对象中的参数selector
- new jQuery.fn.init(selector, context);是调用jQuery原型上init方法创建一个jQuery实例对象(JQ对象)。
init = jQuery.fn.init = function (selector, context, root) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if (!selector) {
return this;
}
// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
root = root || rootjQuery;
// Handle HTML strings
if (typeof selector === "string") {
if (selector[0] === "<" &&
selector[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;
// Option to run 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 (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]);
if (elem) {
// Inject the element directly into the jQuery object
this[0] = elem;
this.length = 1;
}
return this;
}
// HANDLE: $(expr, $(...))
} else if (!context || context.jquery) {
return (context || root).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[0] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
// Shortcut for document ready
} else if (isFunction(selector)) {
return root.ready !== undefined ?
root.ready(selector) :
// Execute immediately if ready is not present
selector(jQuery);
}
return jQuery.makeArray(selector, this);
};
[selector] 根据传递的参数不同走不同的逻辑,最后都返回的是一个JQ对象
// HANDLE: $(""), $(null), $(undefined), $(false)
if (!selector) {
return this;
}
当传递的参数selector为null、undefined、""、0、NaN、false时返回一个空JQ对象。
// ?: 只匹配不捕获
// \s* 一个空白字符(包含空格、制表符、换页符)出现0到多次
// (<[\w\W]+>) 分组捕获中的第一个 <任意字符1到多次>
// [^>]*|#([\w-]+) 不为>的字符0到多次 或者 #数字、字母、下划线1到多次
// ([\w-]+)分组捕获中的第二个
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
/*
如果传递的参数selector 是一个字符串
*/
if (typeof selector === "string") {
//这个字符串以"<"开头以">"结束并且字符串长度>=3 类似这种:"<a>"
if (selector[0] === "<" &&
selector[selector.length - 1] === ">" &&
selector.length >= 3) {
//将字符串放到数组中且字符串索引为1
match = [null, selector, null];
} else {
//捕获正则中的第一个元素 捕获到的结果应该是["selector","分组捕获的第一个元素",...]
match = rquickExpr.exec(selector);
}
// match不为空且(match[1]不为空 或者 contex为空) match[1]:代表selector或分组捕获的第一个元素
if (match && (match[1] || !context)) {
// match[1]不为空
if (match[1]) {
//context 是jQuery的一个实例直返回context[0]否则返回context
context = context instanceof jQuery ? context[0] : context;
// Option to run 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 (isFunction(this[match])) {
this[match](context[match]);
// ...and otherwise set as attributes
} else {
this.attr(match, context[match]);
}
}
}
return this;
// 说明是$("#id")这种方式 调用js的document.getElementById(match[2]); match[2]为正则捕获的id
} else {
elem = document.getElementById(match[2]);
if (elem) {
// 根据id获取的元素不为空,将元素放入到JQ对象中,返回 this指向JQ对象(是一个类数组)。
this[0] = elem;
this.length = 1;
}
return this;
}
// HANDLE: $(expr, $(...))
} else if (!context || context.jquery) {
return (context || root).find(selector);
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor(context).find(selector);
}
// HANDLE: $(DOMElement)
}
selector传入参数为string时:选择器(获取元素的类数组集合“$("#id")” 或 HTML字符串$("<aaa>")(创建DOM对象,返回JQ实例,也就是一个类数组集合)
//判断selector是原生的DOM元素对象
else if (selector.nodeType) {
//同样是将字符串放入JQ对象中返回一个类数组
this[0] = selector;
this.length = 1;
return this;
}
原生的DOM元素对象:把原生DOM对象转换为JQ对象 目的是调用JQ原型上的方法
//判断selector是一个函数
else if (isFunction(selector)) {
return root.ready !== undefined ?
root.ready(selector) : selector(jQuery);
}
//=======================================ready
jQuery.fn.ready = function (fn) {
readyList
.then(fn)
.catch(function (error) {
jQuery.readyException(error);
});
return this;
};
$(function(){}) 等同于$(document).ready(函数)
作用:
1.等待页面中的DOM结构都加载完成(DOMContentLoaded),就会触发执行函数;
2.当做闭包,防止全局变量污染;
当传入的是DOM元素、节点集合…:返回一个JQ实例对象(类数组集合)目的是为了可以调用JQ对象原型上的方法
//返回一个数组 把两个数组或者类数拼接在一起
return jQuery.makeArray(selector, this);
- jQuery中的merge方法
// 合并数据/类数组:把两个集合拼接在一起「第一个集合中」,最后返回第一个集合
jQuery.merge = function merge(first, second) {
//获取第二个数组/类数组中的length转换为number类型
var len = +second.length,
j = 0,
i = first.length;
for (; j < len; j++) {
//将第二个数组/类数组中的值追加到第一个数组/类数组中,且改变length值
first[i++] = second[j];
}
// 数组可以自动累加长度,但是类数组不会
first.length = i;
return first;
};
- jQuery中的makeArray方法
// 把类数组转换为数组 $([类数组])
// 把两个数组或者类数拼接在一起
jQuery.makeArray = function makeArray(arr, results) {
//传入第二个参数为空是返回一个空数组
var ret = results || [];
if (arr != null) {
if (isArrayLike(Object(arr))) {
jQuery.merge(ret,
typeof arr === "string" ? [arr] : arr
);
} else {
// 在不清楚是数组还是类数组集合的时候,我们尽可能使用 [].xxx.call(xxx) 借用的方法来调用数组的方法
// 不考虑兼容以及结构是否变化的情况下,也可以Array.from(xxx)都变为数组
[].push.call(ret, arr);
}
}
return ret;
};
- jQuery中的each方法
//遍历数组/类数组/对象中的每一项
jQuery.each = function each(obj, callback) {
var length, i = 0;
if (isArrayLike(obj)) {
length = obj.length;
for (; i < length; i++) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
} else {
for (i in obj) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
return obj;
},
/*
//改良版
var each = function each(obj, callback) {
var length,
i = 0,
item,
index,
result,
keys;
if (isArrayLike(obj)) {
length = obj.length;
for (; i < length; i++) {
item = obj[i];
index = i;
result = callback.call(item, item, index);
// 我们处理了FOR-EACH不支持的“循环结束的控制方式”:回调函数返回false
if (result === false) break;
}
} else {
// 我们考虑FOR IN的BUG
if (obj == null) throw new TypeError('obj not be a null/undefined!');
keys = Object.keys(obj);
typeof Symbol !== "undefined" ? keys = keys.concat(Object.getOwnPropertySymbols(obj)) : null;
length = keys.length;
for (; i < length; i++) {
index = keys[i];
item = obj[index];
result = callback.call(item, item, index);
if (result === false) break;
}
}
return obj;
};
*/
- jQuery中的pushStack方法
- jQuert中 $(xxx).get([index])和$(xxx).eq([index]) get方法和eq方法的区别:
// 原型方法:供实例调用
jQuery.fn = jQuery.prototype = {
jquery: version,
constructor: jQuery,
// 支持负数索引/不传递索引
get: function (num) {
if (num == null) {
return slice.call(this);
}
return num < 0 ? this[num + this.length] : this[num];
},
eq: function (i) {
var len = this.length,
j = +i + (i < 0 ? len : 0);
return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
},
//给JQ对象增加了一个prevObject属性方便操作DOM找到初始状态
pushStack: function (elems) {
var ret = jQuery.merge(this.constructor(), elems);
ret.prevObject = this;
return ret;
},
//直接拿数组的方法来用
push: push,
sort: arr.sort,
splice: arr.splice,
//...
};
get 和eq 方法都是把JQ对象(一般类数组集合) 转换为 原生DOM对象:可以调用浏览器提供的内置的属性和方法。 基于JQ集合中的某个索引获取即可 $(xxx)[index] ->DOM对象。
$(xxx).get([index]) 返回的是 原生DOM对象
$(xxx).eq([index]) 返回的是 新的JQ实例对象可以继续.addClass调用JQ对象原型上的方法。