前言
在自己封装了一个简单的DOM库之后,发现封装一个库还是不难的。
我所理解的jQuery
其实很简单,我们操作某个对象,不返回这个对象,返回操作这个对象的对象。
jQuery 示例:
window.jQuery = function (selectorOrArrayOrTemplate) {
let elements;
if (typeof selectorOrArrayOrTemplate === "string") {
// 传进来的是选择器
elements = document.querySelectorAll(selectorOrArrayOrTemplate);
} else if (selectorOrArrayOrTemplate instanceof Array) {
// 传进来的是数组
elements = selectorOrArrayOrTemplate;
}
//elements就是要操作的元素或者节点
let api = {
// 下面这些函数可以操作 elements
find(selector) {
let array = [];
for (let i = 0; i < elements.length; i++) {
const elements2 = Array.from(elements[i].querySelectorAll(selector));
array.push(...elements2);
}
array.oldApi = this; // this 就是 旧 api
return jQuery(array);
//目的是返回新的api,新的elements值
},
appendTo(selector) {},
addClass(className) {},
end(className) {},
};
return api;
};
可以看到,最后返回的是api,而这个api又包含了属性和方法。
jQuery就是把该有的功能像这样,写进这个库,基本思想不变。
链式调用
所谓链式调用,就是let api = jquery(".text").find(".text1").addClass("red");
为什么可以链式调用,就是因为返回的是本身。
window.jQuery = function (selectorOrArrayOrTemplate) {
let elements;
if (typeof selectorOrArrayOrTemplate === 'string') {
if (selectorOrArrayOrTemplate[0] === "<") {
//创建div
elements = [createElement(selectorOrArrayOrTemplate)]
} else {
//查找div
elements = selectorOrArrayOrTemplate
}
}else if (selectorOrArrayOrTemplate instanceof Array) {
elements = selectorOrArrayOrTemplate
}
function createElement(string) {
const container = document.createElement("template");
container.innerHTML = string.trim();
return container.content.firstChild;
}
//api可以操作elements
return {
jquery: true,
elements: elements,
get(index) {
return elements[index]
},
appendTo(node) {
if (node instanceof Element) {
this.each(el => node.appendChild(el)) // 遍历 elements,对每个 el 进行 node.appendChild 操作
} else if (node.jquery === true) {
this.each(el => node.get(0).appendChild(el)) // 遍历 elements,对每个 el 进行 node.get(0).appendChild(el)) 操作
}
},
append(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) {
children.each(node => this.get(0).appendChild(node))
}
},
find(selector) {
let array = [];
for (let i = 0; i < elements.length; i++) {
const elements2 = Array.from(elements[i].querySelectorAll(selector))
array = array.concat(elements2);
}
array.oldApi = this;//this 就是旧api
return jQuery(array);
},
each(fn) {
for (let i = 0; i < elements.length; i++) {
fn.call(null, elements[i], i);
}
return this;
},
parent() {
const array = [];
this.each((node) => {
if (array.indexOf(node.parentNode) === 1) {
array.push(node.parentNode);
}
})
return jQuery(array);
},
children() {
const array = [];
this, each((node) => {
//
array.push(...node.children);
})
return jQuery(array);
},
print() {
console.log(elements)
},
//闭包:函数访问外部的变量
addClass(className) {
for (let i = 0; i < elements.length; i++) {
const element = element[i];
element.classList.add(className);
}
return this;
},
oldApi: selectorOrArrayOrTemplate.oldApi,
end() {
return this.oldApi;//this就i是新api
},
}
}
window.$ = window.jQuery
贴出来大段代码。
代码很精简,但是理解起来很费功夫。
首先:接受的参数window.jQuery = function (selectorOrArrayOrTemplate)
参数的种类有几种,根据参数的不同,返回不同操作参数的对象。
this:代码中多次看到return this
参见最开头的代码,this就是前面代码的api,api包含操作这个属性的所有方法。
end方法:这一大段代码的最后可以看到有一个方法是end
return this.oldApi
,this代表现在的所有(整个对象)的东西,this.oldApi是操作oldApi的对象
protoType:每一个对象都可以有这些属性,所以会浪费很多内存,jQuery也想到了这一点,把所有共有属性放到protoType,这样就节约了空间。
总结一下
代码简单,理解起来还是不容易的,要自己写出来就更困难了,多练习才能熟能生巧
贴上我的GitHub仓库链接自己封装的jQuery