我爱编程小志的前端之路

jQuery的原理理解及仿写

2018-03-28  本文已影响0人  小志1

先写段代码

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的对象

上一篇下一篇

猜你喜欢

热点阅读