JavaScript入门笔记[5]:jQuery的山寨源码

jQuery的灵感来源,必定是俄罗斯套娃吧

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)
复制代码
相关文章
相关标签/搜索