jQuery的原理理解及仿写

作者: 小志1 | 来源:发表于2018-03-28 17:54 被阅读0次

    先写段代码

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

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

     <ul>
      <li id="item1">选项1</li>
      <li id="item2">选项2</li>
      <li id="item3">选项3</li>
      <li id="item4">选项4</li>
      <li id="item5">选项5</li>
    </ul>
    
    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原型上加
    <ul>
      <li id="item1">选项1</li>
      <li id="item2">选项2</li>
      <li id="item3">选项3</li>
      <li id="item4">选项4</li>
      <li id="item5">选项5</li>
    </ul>
    
    Node.prototype.getSiblings = function(){
      var allChildren = this.parentNode.children
      var array = {
        length:0
      }
      for(let i=0; i<allChildren.length;i++){
        if (allChildren[i] !== this) {
          array[array.length] = allChildren[i]; 
          array.length += 1
        }
      }
      return array
    }
    Node.prototype.addClass = function(classes){
        classes.forEach((value)=>this.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;}
    
    <ul>
     <li id="item1">选项1</li>
     <li id="item2">选项2</li>
     <li id="item3">选项3</li>
     <li id="item4">选项4</li>
     <li id="item5">选项5</li>
    </ul>
    
    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的原理理解及仿写

        本文链接:https://www.haomeiwen.com/subject/ipkucftx.html