jQuery是目前最为长寿的JS函数库,还有日本up主小哥专门拍了一个视频。虽然从网上看到的,都是唱衰jQuery的声音,可是做为一只菜鸡,我仍是从jQuery开始个人框架学习吧。api
在我这只菜鸡看来,jQuery令我感到恐惧的地方,是它的链式操做,不就是套娃么。数组
链式操做的核心在于,jQuery返回的是一个jQuery对象,一个拥有jQuery所有功能的对象。框架
按照上述的说法,我大胆猜想,jQuery的本质是一个构造函数,虽然它不须要使用New
。那么,就试试看复现jQuery的源码吧~ide
window.jQuery = function(selector){
//要返回一个可操做的对象,因此jQuery是一个全局函数
const elements = document.querySelectorAll(selector);
//咱们要建立一个jQuery返回的jQuery对象api
const api ={
//拿addClass功能做为例子,要注意addClass是api对象的功能
addclass(className){
for(let i=0; i<elements.length; i++){
elements[i].classList.add(className);
}
//return api; 运行结束以后返回一个能够调用操做的对象
//由于是返回调用该函数的对象,因此能够使用this
return this;
}
}
return api;
//由于调用jQuery函数的对象是window
//因此此处return this; 返回的是window对象
}
window.$ = window.jQuery;
//这样,咱们的jQuery函数就能够拿$符直接操做了
复制代码
既然咱们找到了链式操做的返回对象,那么咱们能够更近一步,在调用函数以后直接返回一个对象,咱们的jQuery虽然不是一个构造函数,可是经过return,实现了构造对象的功能。函数
window.jQuery = function(selector){
//要返回一个可操做的对象,因此jQuery是一个全局函数
const elements = document.querySelectorAll(selector);
//咱们能够直接返回一个对象
return{
addclass(className){
for(let i=0; i<elements.length; i++){
elements[i].classList.add(className);
}后返回一个能够调用操做的对象
//由于是返回调用该函数的对象,因此使用this
return this;
},
//咱们新增一个find功能
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope范围节点存在,则能够在范围节点的范围内搜索元素
arr = arr.concat(Arry.from(
(scope || document).querySelectorAll(selector)));
}
//问题来了,咱们的find若是返回查找结果的话,岂不是丧失了链式操做
return arr;
}
}
//调用jQuery函数的对象是window
//若是此处return this; 返回的是window对象
}
window.$ = window.jQuery;
复制代码
在咱们简化告终构的jQuery函数中,添加了新的功能find
。学习
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope范围节点存在,则能够在范围节点的范围内搜索元素
arr = arr.concat(Array.from(
(scope || document).querySelectorAll(selector)));
}
//问题来了,咱们的find若是返回查找结果的话,岂不是丧失了链式操做
return arr;
}
复制代码
通过find
查找后,咱们会得到一个新的数组arr,arr包含了咱们所须要的元素,可是直接返回arr,意味着链式操做会终止。ui
咱们不能继续return this;
么?this
若是继续return this;
,咱们返回的是调用find
的对象。spa
咱们要怎么继续链式操做呢?prototype
让arr成为咱们return的jQuery对象。
要怎么让让arr成为咱们return的jQuery对象?
咱们能够套娃,让jQuery再调用一次arr,让arr包装成jQuery对象
✿✿ヽ(°▽°)ノ✿
可是咱们的jQuery只能接收字符串不是么
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope范围节点存在,则能够在范围节点的范围内搜索元素
arr = arr.concat(Array.from(
(scope || document).querySelectorAll(selector)));
}
//问题来了,咱们的find若是返回查找结果的话,岂不是丧失了链式操做
return jQuery(arr);
}
复制代码
可是咱们的jQuery只能接收字符串不是么
咱们能够修改jQuery函数吖
window.jQuery = function(selectorOrArry){
let elements;
//const只支持在声明时为变量赋值,因此这里采用let
if(selectorOrArry === "string"){
//咱们对输入进行判断,若是是字符串就当作选择器
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
//若是输入是数组,就进行套娃
elements= selectorOrArry;
}
return{
//这里就是咱们所返回的jQuery元素
}
}
window.$ = window.jQuery;
复制代码
至此,咱们实现了jQuery的基本链式套娃操做。
强大的jQuery链式套娃操做就这么简单吗?不不不,咱们的套娃怎会如此善罢甘休。jQuery真正厉害的地方,在于提供了end()
,能够返回上一次操做的jQuery对象。
既然end()
太骚了这么厉害,咱们也要努力山寨一下。
window.jQuery = function(selectorOrArry){
let elements;
//const只支持在声明时为变量赋值,因此这里采用let
if(selectorOrArry === "string"){
//咱们对输入进行判断,若是是字符串就当作选择器
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
//若是输入是数组,就进行套娃
elements= selectorOrArry;
}
return{
oldApi: selectorOrArry.oldApi,
//咱们给返回的jQuery对象新增oldApi属性
end(){
//end只须要返回oldApi
return this.oldApi;
},
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope范围节点存在,则能够在范围节点的范围内搜索元素
arr = arr.concat(
Array.from((scope || document).querySelectorAll(selector)));
}
arr.oldApi = this;
//咱们将须要存档的jQuery对象做为属性增长到arr对象上
//虽然arr是数组,数组也是对象,也有属性
return jQuery(arr);
//这样,咱们在套娃的时候,就会将oldApi一并返回
}
}
}
window.$ = window.jQuery;
复制代码
截止目前,咱们基本山寨完成了jQuery的套娃操做。可是若是每次操做都新增一个jQuery对象,也就意味着每一个对象都会重复建立一样的对象属性,占据额外的空间。
若是引入对象的原型链,咱们的jQuery就更像一个构造函数了就能够更高效了。
window.jQuery = function(selectorOrArry){
let elements;
if(selectorOrArry === "string"){
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
elements= selectorOrArry;
}
//咱们新建一个返回的jQuery对象:api,该对象能够直接调用 jQuery.prototype
const api = Object.creat(jQuery.prototype);
//api须要有elements 和 oldApi属性
api.elements = elements;
api.oldApi = selectorOrArry.oldApi;
return api;
}
window.$ = window.jQuery;
//设置jQuery的原型对象
jQuery.prototype = {
constructor: jQuery,
end(){return this.oldApi;},
find(selector,scope){
const arr = [];
for(let i=0;i<this.elements.length;i++){
//find函数不能直接调用jQuery函数内的对象,只能调用调用find属性的对象的属性
arr = arr.concat(
Array.from((scope || document).querySelectorAll(selector)));}
arr.oldApi = this;
return jQuery(arr); }
}
复制代码
至此,咱们实现了jQuery的链式操做山寨源码。让咱们来欣赏一下jQuery源码又是怎么处理的:
(function(root){
var jQuery = function{
//返回对象,jQuery实现对象的建构以及原型的继承
return new jQuery.prototype.init();
}
jQuery.prototype{
init(){ }
}
//原型共享
jQuery.prototype.init.prototype = jQuery.prototype;
root.$ = root.jQuery = jQuery;
})(this)
复制代码