jQuery 的设计思想

137 阅读2分钟

jQuery 的设计思想可以简述为:选择网页的某个元素,然后对其进行链式操作

一、获取和创建 jQuery 元素

  根据输入参数的类型,进行相应的操作:

  • 如果输入的是字符串且包括"<",如 "<div>" ,就创建一个新元素,返回的 elements 是一个数组;

image.png

  • 如果输入的是字符串是"#xxx"、".xxx"、"xxx" 等,则返回一个节点集合 NodeList; image.png
  • 如果返回的参数是数组,就创建一个新的 Api,后面会提及。 image.png
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"。另外,要区分 HTMLCollectionNodeList,它们分别为元素的集合和节点的集合,其中,节点的集合包括元素的集合、文本的集合、注释的集合等。

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: 代码链接

参考文章