jQuery相关

153 阅读3分钟

从零构建jQuery

第一步

window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    const api = {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return null
        }
    }
    return api
}

这样调用jQuery('.red')会返回api对象,
api对象中可以调用addClass,即jQuery('.red').addClass('blue')
代码逻辑:找到selector选择中的元素并赋值给elements,返回api对象,api对象中addClass方法对elements进行操作

第二步

window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    const api = {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return api
        }
    }
    return api
}

虽然只改变了addClass的返回值,但是这样就可以
jQuery('.red').addClass('blue').addClass('green')
代码逻辑:只要通过jQuery()函数调用,接下来你用不用addClass函数都会返回到api对象

第三步

window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    const api = {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return this
        }
    }
    return api
}

api调用的addClass
小知识:对象中addClass:function(){}等价于上面

第四步

window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    return {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return this
        }
    }
}

只要你调用了jQuery最后你总会得到一个无名对象(api)里面有函数
现在我们用jQuery对象来指代jQuery函数创造出来的对象

第五步

实现find功能,之前四步实现了根据选择器找到元素并给找到的元素增加类名

window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    return {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i = 0;i<elements.length;i++){
                array = array.concat(
                    Array.from(
                        elements[i].querySelectorAll(selector)
                    )
                )
            }
            return this
        }
    }
}

现在可以实现jquery(".red").find(".blue")
注意:Array.from() 方法从一个类似数组或可迭代对象创建一个新的,以及querySelector在找不到时会返回null而不是空数组

第六步

第五步出现了问题,jquery(".red").find(".blue").addClass(".green")最后的green是add到elements上的而不是array上等价于jquery(".red").addClass(".green")
下面解决让addClass操作的是array

window.jQuery = function(selector){
    let elements = document.querySelectorAll(selector)
    return {
        addClass(className){
            for(let i = 0;i<elements.length;i++){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i = 0;i<elements.length;i++){
                array = array.concat(
                    Array.from(
                        elements[i].querySelectorAll(selector)
                    )
                )
            }
            elements = array
            return this
        }
    }
}

首先我们想到就是修改elements

const api1 = jQuery('.test')
api1.addClass('blue')
const api2 = jQuery('.test').find('.child').addClass('.red')
api1.addClass('.green')

但是这样就会出现问题如以上代码不能在退回去用api1了

第七步

window.jQuery = function(selectorOrArray) {
    let elements
  if (typeof selectorOrArray === "string") {
     elements = document.querySelectorAll(selectorOrArray);
  } else if (selectorOrArray instanceof this.Array) {
    elements = selectorOrArray;
  }
  return {
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    },
    find(selector) {
      let array = [];
      for (let i = 0; i < elements.length; i++) {
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
      }
      const newApi = jQuery(array);
      return newApi;
    }
  };
};

理解关键就在于你的elements是什么你的api就在操作什么
代码逻辑:你使用jQuery('.test').find('.child')首先创建旧elements然后创建array,在旧elements中找到符合条件的赋值给array,然后将array赋值给新elements,那么现在在addClass就是给新elements加类了
如果我们把旧的elements对应的api保存下来那么我们就可以来回操作

第八步

window.jQuery = function(selectorOrArray) {
  let elements;
  if (typeof selectorOrArray === "string") {
    elements = document.querySelectorAll(selectorOrArray);
  } else if (selectorOrArray instanceof this.Array) {
    elements = selectorOrArray;
  }
  return {
    oldApi : selectorOrArray.oldApi,
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    },
    find(selector) {
      let array = [];
      for (let i = 0; i < elements.length; i++) {
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
      }
      array.oldApi = this
      return jQuery(array);
    },
    end(){
      return this.oldApi
    }
  };
};

加入end()方法,之前把oldApi挂在array上面,array传给selectorOrArray,end()返回this.oldApi

第九步

window.jQuery = function(selectorOrArray) {
  let elements;
  if (typeof selectorOrArray === "string") {
    elements = document.querySelectorAll(selectorOrArray);
  } else if (selectorOrArray instanceof this.Array) {
    elements = selectorOrArray;
  }
  return {
    oldApi: selectorOrArray.oldApi,
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    },
    find(selector) {
      let array = [];
      for (let i = 0; i < elements.length; i++) {
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
      }
      array.oldApi = this;
      return jQuery(array);
    },
    end() {
      return this.oldApi;
    },
    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);
        }
      });
      array.oldApi = this
      return jQuery(array);
    },
    children(){
      const array = []
      this.each((node)=>{
        array.push(...node.children)
      })
      array.oldApi = this
      return jQuery(array)
    }
  };
};

each函数,这种内调函数逻辑是这样的:each接受一个函数参数称为fn,fn在each内部调用,fn接受一个参数放你想放的东西,fn返回值或执行语句一定和这个参数有关这样调用才有意义

总结

  • elementsOrArray只可以接受传来的值然后赋值给elements,不可以在方法里改变它要改只能返回新api,每一个elements对应一个api,api里包含着上一个api
  • jQuery并不返回选择到的元素,而是返回可以操作这些元素的api