自己动手构造API理解jQuery

2019-04-10  本文已影响0人  kiterumer

封装两个函数,一个用来获取某个节点的兄弟姐妹,一个用来给元素添加类
现在有一个无序列表。循序渐进,体会jQuery设计思想。

<!-- ul>li[id=item$]{选项$}*5 -->
    <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>

封装两个函数

封装一个获取兄弟姐妹函数getSiblings

function getSiblings(node){  /* API */
    var allChildren = node.parentNode.children ;
    // 伪数组
    var array = {
        length: 0
    }
    // 获取node的兄弟姐妹
    for (let i = 0; i < allChildren.length; i++){
        if(allChildren[i] !== node){
            // array是伪数组,没有push方法
            array[array.length] = allChildren[i];
            array.length += 1;
        }
    }
    return array
}

封装添加类函数addClass

//添加的类用数组表示
function addClass(node,classes){
    classes.forEach(value => {
        node.classList.add(value)
    });
}

命名空间

两个函数不能就这么随意的放着,赋予一个命名空间

// getSiblings,addClass作为yyydom对象的方法
window.yyydom = {}
yyydom.getSiblings = getSiblings
yyydom.addClass = addClass

yyydom.addClass(item2,['a','b'])
//这种调用方式并不好,我们希望将node节点放在前面

把node放在前面进行调用

有两种方法:

  1. 扩展 Node 接口,直接在 Node.prototype 上加函数
  2. 自己创建新的接口

1.篡改Node.prototype,添加函数

Node.prototype.getSiblings = function(){
 var allChildren = this.parentNode.children ;
    // 伪数组
    var array = {
        length: 0
    }
    // 获取node的兄弟姐妹
    for (let i = 0; i < allChildren.length; i++){
        if(allChildren[i] !== this){
            // array是伪数组,没有push方法
            array[array.length] = allChildren[i];
            array.length += 1;
        }
    }
    return array
}

Node.prototype.addClass2 = function(classes){
 classes.forEach(value => {
        this.classList.add(value)
    });
}
// 用call显示this,call的第一个参数就是this
// item3.getSiblings()
// item3.getSiblings.call(item3) ,等价上
// item3.addClass(['q','w','e'])
// item3.addClass(item3,['q','w','e'])  等价上

但是,在Node原型上增加方法也不好,如果有多个人修改容易造成冲突。既然这样,不如自己创建一个新Node构造函数。自己动手,丰衣足食。

2.自己创建新的接口(无侵入)

window.Node2 = function(node){
    return {
        getSiblings:function(){
            var allChildren = node.parentNode.children ;
            // 伪数组
            var array = {
            length: 0
            }
            // 获取node的兄弟姐妹
            for (let i = 0; i < allChildren.length; i++){
            if(allChildren[i] !== node){
            // array是伪数组,没有push方法
            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(['p','o'])

构造函数Node2返回一个新对象,这个对象有getSiblings和addClass两个方法,里面还是调用的原生API。似乎离目标越来越近了。

Node2是随意取的,改为jQuery,于是

var node2 = jQuery(item3)
node2.getSiblings()

再进一步,window.$ = jQuery,再省略window,于是就变成了

var node2 = $(item3)
node2.getSiblings()

是不是很熟悉呢?

选择器多样化

目前我们所创建的构造函数只能获取节点,如果是其他选择器呢,比如$('ul>li:nth-child(3)'),那么需要加一些判断

window.jQuery = function(nodeOrSelector){
    //是否为字符串
    if(typeof nodeOrSelector === 'string'){
  //利用了原生的document.querySelector
        node = document.querySelector(nodeOrSelector)
    }else{
        node = nodeOrSelector
    }

    return {
        getSiblings:function(){
            var allChildren = node.parentNode.children ;
            // 伪数组
            var array = {
            length: 0
            }
            // 获取node的兄弟姐妹
            for (let i = 0; i < allChildren.length; i++){
            if(allChildren[i] !== node){
            // array是伪数组,没有push方法
            array[array.length] = allChildren[i];
            array.length += 1;
            }
            }
            return array
        },
        addClass:function(classes){
            classes.forEach(value => {
                node.classList.add(value)
            });
        }
    }

}

//获取列表第五个元素
var node2 = jQuery('ul>li:nth-child(5)')
// 给我旧的对象,返回新的对象
node2.addClass(['red'])

那么要获取多个节点呢,这是里面就要用到document.querySelectorAll,而且部分逻辑也有变化

window.jQuery2 = function(nodeOrSelector){
    let nodes  //最后要返回的对象
    if(typeof nodeOrSelector === 'string'){
        nodes = document.querySelectorAll(nodeOrSelector) //伪数组
    }else if(nodeOrSelector instanceof Node){
        // 保证返回结果一致,都为伪数组
        nodes = {
            0: nodeOrSelector,
            length: 1
        }
    }
   //添加类
    nodes.addClass = function(classes){
        classes.forEach((value) => {
            // 给nodes数组里每一项添加类
            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
        }
    }
    // 两者合并,这种在jQuery中非常常见
    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
            }
        }
    }
    return nodes
}
var node3 = jQuery2('ul>li')
node3.addClass(['red'])
node3.text('yyy')
//这大概就是最终体了
上一篇下一篇

猜你喜欢

热点阅读