JQuery可以理解为就是一个函数,通过传入一个节点或者选择器,返回一个新的对象,这个对象有构造好的API,通过调用这个API就可以得到我们想要的操作DOM的结果。而对象中构造好的API本质上是通过封装一个一系列操作原生DOM的方法,而我们只需要从外面调用这个方法名不用具体管封装的源码是如何实现。
我们简单的用函数封装两个原生DOM操作的功能,1、getSiblings 获取到所有的兄弟元素;2、addClass 给元素添加一个类名。

function getSiblings(node){
  let allChildren = node.parentNode.children
  
  let array = {
    length:0
  }
  for(let i =0; i<allChildren.length; i++){
    if(allChildren[i] !== node){
      array[array.length] = allChildren[i]
      array.length++
    }
  }
  return array
}
console.log(getSiblings(item3))

function addClass(node,classes){
  for (let key in classes){
    let value = classes[key]
    if(value) {
      node.classList.add(key)
    }else {
      node.classList.remove(key)
    }
  }
}
addClass(item3,{a:true,b:false,c:true})

接下来将这两个函数装在名叫hy的对象中,通过hy.getSiblings()进行调用,而不是直接调用getSiblings(),这样做的好处:1、不会覆盖方法、不会重名,hy是自己取的一个名字。2、可通过命名为其划分功能,同一类功能的或者处理同一个模块的可以放在一个对象中,提升代码的可阅读性。
这种方法简称——命名空间,是设计模式的一种

// 命名空间
window.hy = {}
hy.getSiblings = function (node) {
  let allChildren = node.parentNode.children
  
  let array = {
    length:0
  }
  for(let i =0; i<allChildren.length; i++){
    if(allChildren[i] !== node){
      array[array.length] = allChildren[i]
      array.length++
    }
  }
  return array
}
hy.addClass = function (node,classes) {
  for (let key in classes){
    let value = classes[key]
    let methodName = value ? 'add':'remove'
      node.classList[methodName](key)
      node.classList[methodName](key)
  }
}
hy.addClass(item3)
hy.addClass(item3,{a:true,b:false,c:true})

通过命名空间调用函数或许你仍觉得调用方法不够优雅,我们还可以再进行改造。将方法加入到原型中,通过原型链调用,可直接item3.getSiblings() 这样调用,比起hy.getSiblings(item3)是不是优雅许多。但是也有缺点:污染了原型,如果大家都往原型中添加自己库的方法,那么原型中的方法名很有可能会被覆盖,导致BUG。

//调用原型链,将getSiblings()方法添加到prototype原型中
Node.prototype.getSiblings = function(){
  let allChildren = this.parentNode.children
  let array = {
    length:0
  }
  for(let i =0; i<allChildren.length; i++){
    if(allChildren[i] !== this){
      array[array.length] = allChildren[i]
      array.length++
    }
  }
  return array
}

Node.prototype.addClass = function(classes){
for (let key in classes){
    let value = classes[key]
    let methodName = value ? 'add':'remove'
      this.classList[methodName](key)
      this.classList[methodName](key)
  }
}
item3.getSiblings()
item3.addClass({a:true,b:false,c:true})

于是最好的方法是新创建一个对象,将我们需要添加的方法加入到这个对象中,如下方代码。

window.Node2 = function(node){
  return {
    getSiblings:function(){
      let allChildren = node.parentNode.children
      let array = {
        length:0
      }
      for(let i =0; i<allChildren.length; i++){
        if(allChildren[i] !== node){
        array[array.length] = allChildren[i]
        array.length++
      }
    }
    return array
   },
    addClass:function(node,classes){
      for (let key in classes){
        let value = classes[key]
        let methodName = value ? 'add':'remove'
        node.classList[methodName](key)
        node.classList[methodName](key)
      }
    }
  }
}

let node2 = Node2(item3)
node2.getSiblings()
node2.addClass(item3,{a:true,b:false,c:true})

重新给函数命名

window.$ = jQuery
 var $div = $(‘div’)


看,2个JQuery的API就实现了!

window.jQuery = function(nodeOrSelector){
  let nodes = {}
  if (typeof nodeOrSelector ==='string'){//如果是多个选择器
    let temp = document.querySelectorAll(nodeOrSelector)//伪数组
    for(let i=0; i<temp.length; i++){
      nodes[i] = temp[i]
    }
    nodes.length = temp.length
  } else if (nodeOrSelector instanceof Node) {//如果是一个节点
    nodes = {
      0:nodeOrSelector,
      length:1
    }
  }
  
  nodes.getSiblings = function(){
    
  }
  nodes.addClass = function(classes){
    classes.forEach((value)=>{
      for(let i=0; i<nodes.length; i++){
        nodes[i].classList.add(value)
      }
    })
  }
  return nodes
}

window.$ = jQuery
var $div = $('div')
$div.addClass('red') // 可将所有 div 的 class 添加一个 red
$div.setText('hi') // 可将所有 div 的 textContent 变为 hi

当然,真正的JQuery源码实现起来要复杂的多,我的本意是通过简化版的JQuery来理解JQuery的设计思想,从而更好地在开发中借鉴运用。