封裝函數到實現簡化版jQuery

需求分析

  • 獲取一個節點的全部兄弟;
  • 給一個節點添加加多個 class
  • DOMAPI 實現比較繁瑣,因此本身封裝 API

功能實現

1.封裝函數

  • 獲取兄弟 操做步驟: 在 html 中有一個 ul 標籤,在 ul 中有 5li
<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>
複製代碼
  • 獲取 iditem3 的兄弟元素。 首先定義一個 allChildren 變量來存儲 item3 的父節點全部的子元素;
var allChildren = item3.parentNode.children;
複製代碼

而後定義一個空數組來存兄弟元素;html

var array = {length:0};
複製代碼
  • 遍歷全部的孩子節點,若是不是 item3 ,那麼就存到 array 僞數組中。
for(let i = 0;i < allChildren.length;i++){
  if(allChildren[i] !== item3){
    array[array.length] = allChildren[i];   //數組下標一一對應存儲 item 元素
    array.length+=1;
  }
}
複製代碼

這個 array 是一個僞數組,它的原型鏈直接指向了 Object.protottype 並無指向 Array.prototype (只有原型鏈中指向 Array.prototype 的數組纔是真正的數組。)node

  • 封裝成函數 封裝成一個具名函數,方便調用,return 這個數組,給咱們的函數加一個參數,而後調用這個函數同時傳參item3
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;
}
getSiblings(item3);
複製代碼
  • 再封裝一個給節點添加多個 class 的函數
function addClass = function(node, classes){
  classes.forEach( (value) => node.classList.add(value) );
 };
addClass(item3, ['a','b','c']);
複製代碼

調用這個函數同時傳參,這樣咱們就能夠給 item3 添加3個 class,分別爲 a , b , c設計模式

2.命名空間

如今咱們有兩個 API了,可是它們看起來很分散,咱們有什麼辦法能讓這兩個 API 有關聯呢? 咱們能夠聲明一個變量 window.reChenDom = {};數組

window.reChenDom = {};

reChenDom.getSiblings = function(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;
};

reChenDom.addClass = function(node, classes){
  classes.forEach( (value) => node.classList.add(value) );
 };

reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);
複製代碼

這就叫作命名空間,也是一種設計模式。命名空間是很是有必要的,若是沒有命名空間,有兩個缺點:第一是別人不知道你的庫叫什麼,另外一個是會不知不覺把全局對象給覆蓋了。閉包

3.能不能把 node 放在前面

接下來第三個特色,咱們要用的時候特別麻煩,老是要:函數

reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);
複製代碼

能不能像下面這樣每次使用都方便點呢ui

item3.getSiblings();
item3.addClass( ['a','b','c'] );
複製代碼

有兩種辦法:this

  1. 直接改 Node 的原型(擴展 Node 接口直接在 Node.prototype 上加函數):
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) );
 };

item3.getSiblings();
item3.addClass( ['a','b','c'] );
複製代碼

this就是 getSiblings()item3.addClass( ['a','b','c'] ) 被調用時前面的對象。 其實這個函數寫的很差,爲何呢?這樣寫是在改 Node 的屬性,若是有其餘人也這樣寫就會被覆蓋。spa

4.把 Node2 改個名字吧

  1. jQuery 本身構造的一個函數,調用 Node 版本:
window.jQuery = 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];
        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'] );
複製代碼

這樣就不單單能夠傳 Node,也能夠傳其它的,好比選擇器:‘#item3’,因此這個名字就不叫 node 了:prototype

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];
          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'] );
複製代碼

在這裏咱們用到了閉包,addClass 這個函數用到了 node,這個 node 不是函數的內部聲明,那這個node 是哪聲明的呢?是在外面,若是一個函數用到了它外面的變量,那麼 node 和這個匿名函數統稱爲閉包。

咱們的 jQuery 能不能再厲害一點呢?若是我想同時操做多個 li,給這些 li 添加 class,怎麼辦呢,這個時候就不能叫 node 了,要叫 nodes,以前的結構已經不適用了。新結構:

window.jQuery = function (nodeOrSelector){
  let nodes = {};
    if( typeof nodeOrSelector === 'string'){
      var 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);
      };
    });
  };
  return nodes;
};

var nodes = jQuery('ul>li')
nodes.addClass( ['red'] );
複製代碼

5.再給個 alias

window.$ = window.jQuery
window.$ = function (nodeOrSelector){...}
var $nodes = $('ul>li')   //在變量前加上一個 $, 防止變量弄混
複製代碼

那如今還能夠添加幾個有用的 jQueryAPI,好比說獲取/設置元素的文本:

window.$ = function (nodeOrSelector){
  let $nodes = {};
    if( typeof nodeOrSelector === 'string'){
      var 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.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 $nodes = $('ul>li')
$nodes.addClass( ['red'] );
$nodes.text('hi');    //若是不給參數說明是獲取text,若是給了一個參數說明是設置text
複製代碼

這樣咱們就從封裝兩個函數到實現了簡化版的 jQuery,添加 class 和獲取/設置文本的 API

相關文章
相關標籤/搜索