jQuery的原理理解及仿写

先写段代码

var dom = {}
dom.getSiblings(node)
dom.addClass(node, {a: true, b: false})

dom就是命名空间,一个对象,对后面的封装和理解有帮助

 
  • 选项1
  • 选项2
  • 选项3
  • 选项4
  • 选项5
var allChildren = item3.parentNode.children var array = {length:0} for(let i = 0;i < allChildren.length; i++){ if(allChildren[i] !== item3){ array[array.length] = allChildren[i]; //不是数组没有push方法 array.length += 1 } } console.log(array)

上面的代码实现的效果是获得节点的兄弟姐妹
console.log输出的是除了item3以外的其他兄弟节点,即item1, #item2, item4, #item5

接下来把代码进行封装

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

接下来增加实现删除添加class类功能的代码

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

我们通过审查元素item3就可以看到item3上多了abclass类,
封装删除添加class类的代码

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

将上面两个方法代码合并整合,来实现同样的效果

function getSiblings(node) {
  var allChildren = node.parentNode.children
  var array = {
    length: 0
  }
  for (let i = 0; i < allChildren.length; i++) {
    if (allChildren[i] !== node) {
      array[array.length] = allChildren[i]; //不是数组没有push
      array.length += 1
    }
  }
  return array
}

function addClass(node,classes) {
  for (let key in classes) {
    var value = classes[key]
    var methodName = value ? 'add': 'remove' 
    node.classList[methodName](key)
  }
}

window.xzdom = {}
xzdom.getSiblings = getSiblings
xzdom.addClass = addClass
xzdom.getSiblings(item3)
xzdom.addClass(item3,{'a':true,'b':false,'c':true})

上面代码的最后五行就命名空间, 是一种设计模式
命名空间非常有必要,如果没有命名空间有两个缺点1.别人不知道叫什么2.会不知不觉覆盖全局变量。
而jQuery就是用了命名空间的模式来实现的。
下面正式引出了jQuery的设计原理

首先我们在原型上加,Node.prototype原型上加
  • 选项1
  • 选项2
  • 选项3
  • 选项4
  • 选项5
Node.prototype.getSiblings = function(){ var allChildren = this.parentNode.children var array = { length:0 } for(let i=0; ithis.classList.add(value) ) } //移除class的功能取消了,保留了addclass功能 console.log( item3.getSiblings() ) // 等价于 console.log(item3.getSiblings.call(item3)) //this作为第一个参数 item3.addClass( ['a','b','c'] ) // 等价于 item3.addClass.call(item3, ['a','b','c'] ) //['a','b','c']作为第二个参数

这里实现了在node原型上的添加class的功能,但是有个问题,它会覆盖全局变量的方法,比如原型上有getSiblings这个方法或者属性,那么这里就会覆盖掉。

其次通过修改名称来保留原来的方法或属性---新建一个Node2(或者叫jQuery)
window.jQuery = function(node) {   //window.Node2 = function(node) {
  return {
    getSiblings: function() {
      var allChildren = node.parentNode.children
      var array = {
        length: 0
      }
      for (let i = 0; i < allChildren.length; i++) {
        if (allChildren[i] !== node) {
          array[array.length] = allChildren[i]; //不是数组没有push
          array.length += 1
        }
      }
      return array
    },
    addClass: function(classes) {
      classes.forEach((value) => node.classList.add(value))
    }
  }
}

var node2 = jQuery(item3)    //var node2 = Node2(item3)
console.log(node2.getSiblings())
node2.addClass(['a', 'b', 'c'])

代码就实现了在item3上添加了a,b,c三个class
Node2(jQuery)接受一个旧的节点,然后返回一个新的对象,这个对象就是Node2(jQuery)对象

下面加上选择器
.blue{color:blue}

window.jQuery = function(nodeOrSelector) {
  let node
  if(typeof nodeOrSelector === 'string'){
    node = document.querySelector(nodeOrSelector)
  }else{
    node = nodeOrSelector
  }
  return {
    getSiblings: function() {
      var allChildren = node.parentNode.children
      var array = {
        length: 0
      }
      for (let i = 0; i < allChildren.length; i++) {
        if (allChildren[i] !== node) {
          array[array.length] = allChildren[i]; //不是数组没有push
          array.length += 1
        }
      }
      return array
    },
    addClass: function(classes) {
      classes.forEach((value) => node.classList.add(value))
    }
  }
}
var node2 = jQuery('ul>li:nth-child(3)')     // var node2 = jQuery('#item3')
console.log(node2.getSiblings())
node2.addClass(['blue', 'b', 'c'])

代码实现了在item3上添加bluebc三个class类,并且blue类有样式,并且item3li变蓝

同时操作6个li
.blue{color:blue;}

  • 选项1
  • 选项2
  • 选项3
  • 选项4
  • 选项5
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.addClass = function(classes) { classes.forEach((value) => { for (let i = 0; i < nodes.length; i++) { nodes[i].classList.add(value) } }) } nodes.getText = function() { var texts = [] for (let i = 0; i < nodes.length; i++) { texts.push(nodes[i].textContent) } return texts } nodes.setText = function(text) { for (let i = 0; i < nodes.length; i++) { nodes[i].textContent = text } } return nodes } var node2 = jQuery('ul > li') node2.addClass(['blue']) console.log(node2.getText()) node2.setText('hi')

通过全局变量的jQuerysetText方法就把6个li变蓝了

getTextsetText方法放在同一个函数封装
nodes.text = function(text) {
    if (text === undefined) {          //元素的文本内容
      var texts = []
      for (let i = 0; i < nodes.length; i++) {
        texts.push(nodes[i].textContent)
      }
      return texts
    } else {                              //替换文本内容,并用传进来的值覆盖
      for (let i = 0; i < nodes.length; i++) {
        nodes[i].textContent = text
      }
    }
  }

这里的设置文本内容和jQuery很像,传一个参数,如果是undefined,就获取元素本是的文本内容;如果有内容就替换文本内容,用用传进来的值覆盖。

我们可以看出 jQuery是函数(有括号,6种数据类型不符合,只能是对象里的函数这个类型符合)。并且是链式操作,因为上面的代码在node的原型链上线指向了构造出来的jQuery的原型,然后再指向Object.prototype

习俗:如果这个对象是由jQuery构造出来的或者是$构造出来的,就在对象前面加$,表示它是jQuery的对象

你可能感兴趣的:(jQuery的原理理解及仿写)