实现一个jQuery的API

1. 封装函数

1.1 获取node的兄弟元素

1.1.1 原始代码

<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>

let allChildren = item3.parentNode.children
let array = {
  length: 0
}
for (let i = 0; i < allChildren.length; i++) {
  if (allChildren[i] !== item3) {
    array[array.length] = allChildren[i]  //给伪数组添加子元素
    array.length += 1
  }
}
console.log(array)  // 得到items3的兄弟元素的伪数组
复制代码

1.1.2 封装函数,得到API,getSiblings()

function getSiblings(node) {  // 这个这就是API 
    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 += 1
      }
    }
    return array
}
console.log(getSiblings(item3))  // 得到items3的兄弟元素
复制代码

1.2 写第二个函数,给元素添加样式

1.2.1 原始代码

let classes = {'a':true,'b':false,'c':true}
    for (let key in classes) { // 遍历这个哈希表
        let value = classes[key]
        if (value) {    // value值为真则添加,假则删除
            node.classList.add(key)
        } else {
            node.classList.remove(key)
        }
    }

复制代码

1.2.2 封装函数, 得到第二个API,addClass()

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})   
复制代码

1.2.3 代码优化

只要有相似的代码,就有优化的空间 。

function addClass(node, classes) {
    for (let key in classes) {
        let value = classes[key]
        let methodName = value ? 'add':'remove'  //如果value值为真,methodName为add,假则为remove
            node.classList.['methodName'](key)
    }
}
ffdom.addClass(item3, {a:true,b:false,c:true})
复制代码

1.2.4 改成数组的形式

function addClass(node, classes) {
    classes.forEach((value)=> node.classList.add(value))
}
addClass(item3, ['a','b','c'])
复制代码

2. 关联两个独立的函数 命名空间

利用命名空间的设计模式,关联两个独立的函数,这样还可以防止代码覆盖的问题。

window.xxxxdom = {}  /*yui使用的就是命名空间的模式*/
xxxxdom.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]   // 伪数组没有push()方法,利用length来添加元素
        array.length += 1
      }
    }
    return array
}
xxxxdom.addClass = function(node, classes) {
    classes.forEach((value)=> node.classList.add(value))
}

xxxxdom .getSiblings(items)
xxxxdom .addClass(item3, ['a','b','c'])
复制代码

3. 去掉xxxxdom

去掉xxxxdom,改写成以下模式,我们有两种方法。

item3.getSiblings()
item3.addClass(['a','b','c'])
复制代码

3.1 第一种方法,直接改node的原型

这样做的缺点:会产生代码覆盖的问题。

Node.prototype.getSiblings = function() {
    let allChildren = this.parentNode.children    // this 隐式指向 Node
        let 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))
}

console.log(item3.getSiblings()) //  得到一个item3的兄弟元素的伪数组
item3.addClass(['a','b','c'])
复制代码

3.1.1 函数调用,用call与不用call的区别

用call就是显示指定this,this是第一个参数 不用是显示隐式指定this

item3.getSiblings.call(item3)
item3.addClass.call(item3,['a','b','c'])
复制代码

3.2 第二种方法,自己做个构造函数,

自己创造一个构造函数,就是可以解决代码覆盖的问题。

3.2.1 思路

创造一个Node2的构造函数,它接收一个node参数,返回一个哈希表,它的key就是我们的想要的api。使用时,传入node参数,就可以使用了。

window.Node2 = function(node) {
    return {
        getSiblings: function() {
            let allChildren = node.parentNode.children   // node  是传进来的节点参数
            let 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
        }
        addClass: function(classes) {
            classes.forEach((value)=> node.classList.add(value))
        }
    }
}
var node2 = Node2(item3)
node2.getSiblings()
node2.addClass(['a','b','c']))
复制代码

3.2.1 将Node2改为jQuery

将Node2改为jQuery,jQuery接收一个旧的dom,返回一个新的对象,这个新的对象有新的api,内部实现还是调用旧的api,只不过好用了一些。jQuery实质上是一个构造函数,接收一个参数,这个参数可以使节点或选择器,然后返回一个方法对象去操作节点。

window.jQuery = 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 += 1
              }
            }
            return array
        }
        addClass: function(classes) {
            classes.forEach((value)=> node.classList.add(value))
        }
    }
}
var node2 = jQuery(item3)
node2.getSiblings()
node2.addClass(['a','b','c']))
复制代码

3.2.2 改进这个jQuery,传参改进

传参node可以不只是node还可以是选择器selector,只需要进行类型检测即可。

window.jQuery = function(nodeOrSelector) {
    let node
    if(typeof nodeOrSelector === 'string') {    // 类型检测
        node = document.querySelector(nodeOrSelector)
    } else {
        node = nodeOrSelector
    }
    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 += 1
              }
            }
            return array
        }
        addClass: function(classes) {
            classes.forEach((value)=> node.classList.add(value))
        }
    }
}
var node2 = jQuery('#item3')      // 可以通过选择器来找到node了
node2.getSiblings()
node2.addClass(['red','b','c']))

相关css
css    
.red {color: red;}
复制代码

3.2.3 改进这个jQuery,操作多个节点

window.jQuery = function(nodeOrSelector) {
    let nodes
    if(typeof nodeOrSelector === 'string') {
        let temp = document.querySelectorAll(nodeOrSelector)  // 伪数组
        for(let i=0;i<temp.length;i++) {
         node[i] = temp[i]
        }
        nodes.length = temp.length    // 利用temp去掉多余的原型,直接指向object.prototype
    } else if(nodeOrSelector instanceof Node) {
        nodes = {0:nodeOrSelector, length:1}
    }
    
    nodes.getSiblings = function() {}
    nodes.addClass = function() {}
    
    return nodes
}
var node2 = jQuery('ul > li')
console.log(node2)
复制代码

3.2.4 改进这个jQuery,给nodes添加方法

window.jQuery = function(nodeOrSelector) {
    let nodes
    if(typeof nodeOrSelector === 'string') {
        let temp = document.querySelectorAll(nodeOrSelector)  // 伪数组
        for(let i=0;i<temp.length;i++) {
         node[i] = temp[i]
        }
        nodes.length = temp.length  object.prototype
    } else if(nodeOrSelector instanceof Node) {
        nodes = {0:nodeOrSelector, length:1}
    }
    
    nodes.addClass = function() {      //添加addClass()方法
        classes.forEach((value)=> {
            for(let i = 0; i < nodes.length; i++) {
                node[i].classList.add(value)
            }
        }
    }
    
    return nodes
}
var node2 = jQuery('ul > li')
node2.addClass(['blue'])
复制代码

3.2.5 改进这个jQuery,给nodes添加其他api

添加getText()和setText()

window.jQuery = function(nodeOrSelector) {
    let nodes
    if(typeof nodeOrSelector === 'string') {
        let temp = document.querySelectorAll(nodeOrSelector)  // 伪数组
        for(let i=0;i<temp.length;i++) {
         node[i] = temp[i]
        }
        nodes.length = temp.length    // 利用temp去掉多余的原型,直接指向object.prototype
    } else if(nodeOrSelector instanceof Node) {
        nodes = {0:nodeOrSelector, length:1}
    }
    
    nodes.addClass = function() {
        classes.forEach((value)=> {
            for(let i = 0; i < nodes.length; i++) {
                node[i].classList.add(value)
            }
        }
    }
    nodes.getText = function() {    // 添加getText()
        var texts = []
        for(let i = 0; i < nodes.length; i++) {
            text.push(nodes[i].textContent)
        }
        return texts
    }
    nodes.setText = function() {     // 添加setText()
        for(let i = 0; i < nodes.length: i++) {
            nodes[i].textContent = texgt
        }
    }
    return nodes
}
var node2 = jQuery('ul > li')
node2.addClass(['blue'])
node2.getText()
node2.setText('hi')
复制代码

3.2.6 优化text() api

window.jQuery = function(nodeOrSelector) {
    let nodes = {}
    if(typeof nodeOrSelector === 'string') {
        let temp = document.querySelectorAll(nodeOrSelector)  // 伪数组
        for(let i=0;i<temp.length;i++) {
         node[i] = temp[i]
        }
        nodes.length = temp.length    // 利用temp去掉多余的原型,直接指向object.prototype
    } else if(nodeOrSelector instanceof Node) {
        nodes = {0:nodeOrSelector, length:1}
    }
    
    nodes.addClass = function() {
        classes.forEach((value)=> {
            for(let i = 0; i < nodes.length; i++) {
                node[i].classList.add(value)
            }
        }
    }
    node2.text = function(text) {     // 将getText()和setText()合并
        if(text === undefined) {
           var texts = []
           for(let i = 0; i < nodes.length; i++) {
               text.push(nodes[i].textContent)
           }
           return texts 
        }
    } else {
       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.text())
node2.text('hi')

复制代码

4. 总结

jQuery的实现思路:创建一个jQuery构造函数,接收一个node或selector,并把它封装成一个伪数组,然后在这个伪数组上增加几个api,返回这个伪数组。

window.jQuery = function(nodeOrSelector) {
  let nodes = {
    0: nodeOrSelector,
    length: 1
  }
  nodes.addClass = function(classes) {}
  nodes.text = function(text) {}
  return nodes
}

var node2 = jQuery('ul > li')
{0: li, 1: li, length: 5, addClass: f, text: f}

node2.addClass(['blue'])
node2.text()
node2[0].classList.add('blue')
复制代码

jQuery的优点:

  • jQuery 在兼容性方面做得很好,1.7 版本兼容到 IE 6。
  • jQuery 还有动画、AJAX 等模块,不止 DOM 操作。
  • jQuery 的功能更丰富。

5. 参考链接

饥人谷jQeury相关课程https://xiedaimala.com/

转载于:https://juejin.im/post/5ab90862518825557208622e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值