jQuery 的设计思想可以简述为:选择网页的某个元素,然后对其进行链式操作。
一、获取和创建 jQuery 元素
根据输入参数的类型,进行相应的操作:
- 如果输入的是字符串且包括"<",如
"<div>",就创建一个新元素,返回的 elements 是一个数组;
- 如果输入的是字符串是"#xxx"、".xxx"、"xxx" 等,则返回一个节点集合 NodeList;
- 如果返回的参数是数组,就创建一个新的 Api,后面会提及。
window.$ = window.jQuery = function(selectorOrArrayOrTemplate) {
let elements;
if (typeof selectorOrArrayOrTemplate === "string") {
if (selectorOrArrayOrTemplate[0] === "<") {
// 创建 div
elements = [createElement(selectorOrArrayOrTemplate)];
} else {
// 查找 div
elements = document.querySelectorAll(selectorOrArrayOrTemplate);
}
} else if (selectorOrArrayOrTemplate instanceof Array) {
//
elements = selectorOrArrayOrTemplate;
}
function createElement(string) {
const container = document.createElement("template");
container.innerHTML = string.trim();
return container.content.firstChild;
}
二、使用原型,存放共有属性
由于操作方法的属性(函数)都是一样的,为了减少内存,可以将它们全部放到同一个地方,实现代码如下:
window.$ = window.jQuery = function(selectorOrArrayOrTemplate) {
...
const api = Object.create(jQuery.prototype)
// 创建一个对象,这个对象的 __proto__ 为括号里面的东西
Object.assign(api, {
elements: elements,
oldApi: selectorOrArrayOrTemplate.oldApi
})
// api.elements = elements
// api.oldApi = selectorOrArrayOrTemplate.oldApi
return api
};
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
jquery: true,
...
}
三、添加一个子节点
添加子节点有两种方法,分别为 appendTo(node) 和 append(node)。它们两个的区别是:一个 node 作为父节点,另一个 node 作为子节点。同时,在操作的时候,要注意 node 是一个节点,而不能是一个字符串,比如直接输入 "#id"。另外,要区分 HTMLCollection 和 NodeList,它们分别为元素的集合和节点的集合,其中,节点的集合包括元素的集合、文本的集合、注释的集合等。
appendTo(node) {//node区分是字符串还是节点
if (node instanceof Element) { //元素节点
this.each(el => node.appendChild(el));
} else if (node.jquery === true) { //Api
this.each(el => node.get(0).appendChild(el));
}
},
append(children) { // children 不能是字符串
if (children instanceof Element) { //元素节点
this.get(0).appendChild(children);
} else if (children instanceof HTMLCollection) { //元素集合
for (let i = 0; i < children.length; i++) {
this.get(0).appendChild(children[i]);
}
} else if (children.jquery === true) { //Api
children.each(node => this.get(0).appendChild(node));
}
},
四、寻找元素集合中每个元素的后代
使用该方法时,涉及到的知识点主要为 oldApi 的设置和使用。在对元素进行链式操作的时候,有时会需要返回到上一个 Api,所以需要设置 oldApi,常与方法 end() 一起使用;另一个知识点是,使用该方法时,不能直接返回一个数组,要返回一个 newApi,因为如果返回的是数组,那就无法再进行链式操作,链式操作的关键点为 elements。
find(selector) {
let array = [];
for (let i = 0; i < this.elements.length; i++) {
const elements2 = Array.from(this.elements[i].querySelectorAll(selector));
array = array.concat(elements2); // 不是 this.elements2
}
array.oldApi = this; // this 就是旧 api
return jQuery(array); // newApi
},
end() {
return this.oldApi; // 回到旧Api;this是新api
}
JS Bin: 代码链接
参考文章
- jQuery设计思想 by 阮一峰
- jQuery API 中文文档