从封装函数到实现简易版自用jQuery(一)

温馨提示

本文阅读对象: 对 JavaScript 有一定的了解,如果你没有学过或者忘记 JavaScript 某些操作,请看 阮一峰 JavaScript 教程 。

导语

DOM 有许多 API ,但是有些 API 太难用了。
比如,想获取某 element 的所有兄弟该怎么办?
再比如,我想给某 element 加多个 class ,你说有 node.classList.add( )啊。那我想加 100 个怎么办?总不能写 100 遍吧,so 自己封装个 API 。

向大佬学习

自己写一个毫无头绪啊,那jQuery是怎么做的,我们模仿一下嘛。
在jQuery这个库里,有许多函数,比如addClass( ),.css( ),.data( )等等。也就是说,jQuery是一个大仓库,仓库里有很多工具,我们需要什么的时候就用什么工具。
现在我们要做的就是,自己生产很多工具,然后建立自己的仓库,我们想用什么就从仓库里拿出来什么。

1. 生产工具

假设我们现在需要两个工具,分别是获取兄弟 element 和 添加多个 class 。

实现功能

1. 获取兄弟 element

操作步骤:

  1. 在 html 中有一个 ul 标签,在 ul 中有 5 个 li 。
  • item1
  • item2
  • item3
  • item4
  • item5
  1. 获取 id 为 item3 的兄弟元素。
    首先定义一个 allChildren 变量来存储 item3 的父节点所有的子元素。
    获取子元素 DOM 有两个 API ,node.parent.children 和 node.parent.childNodes 。应该选择哪个呢?

    parent.childNodes:获取节点,不同浏览器表现不同;
    IE:只获取元素节点;
    非IE:获取元素节点与文本节点;
    parent.children:获取元素节点,浏览器表现相同。
    因此建议使用children。

var allChildren = item3.parentNode.children;
从封装函数到实现简易版自用jQuery(一)_第1张图片
image

上图是在某博客页面做的测试,可以看到使用 parent.childNodes 获取到了 text 节点。

  1. 定义一个空数组来存兄弟元素,此时数组长度为0。
var arr = {length:0};
  1. 遍历所有的孩子节点,如果不是 item3 ,那么就存到 arr 数组中。
for(var i = 0;i < allChildren.length;i++){
  if(allChildren[i] !== item3){
    arr[arr.length] = allChildren[i];
    arr.length++;
  }
}

小技巧:使用 arr[arr.length] = allChildren[i]; 使得数组下标依次存储 item 元素。

完整代码

  • item1
  • item2
  • item3
  • item4
  • item5
var allChildren = item3.parentNode.children; var arr = {length:0}; for(var i = 0;i < allChildren.length;i++){ if(allChildren[i] !== item3){ arr[arr.length] = allChildren[i]; arr.length++; } } console.log(arr);

运行结果

从封装函数到实现简易版自用jQuery(一)_第2张图片
image

注意
这个 arr 数组是一个伪数组,它的原型链直接指向了 Object 并没有指向 Array.prototype ,只有原型链中指向 Array.prototype 的数组才是真正的数组。如果原型链中不包含 Array.prototype 是没有数组.push( )等方法的。

封装成函数

  1. 包装一下,加个 function ,同时起个名字,方便调用。
function getSiblings(){
  var allChildren = item3.parentNode.children;
  var arr = {length:0};
  for(var i = 0;i < allChildren.length;i++){
    if(allChildren[i] !== item3){
      arr[arr.length] = allChildren[i];
      arr.length++;
    }
  }
  console.log(arr);
}
  1. 我们给这个函数一个返回值,返回值呢就是这个数组,把 console.log(arr) 改成 return arr
  2. 此时我们发现,item3 这个 id 是函数外面的值,是在调用函数的时候传参才能获取到,所以给我们的函数加一个参数,同时把 item3 改成参数 node 。
function getSiblings(node){
  var allChildren = node.parentNode.children;
  var arr = {length:0};
  for(var i = 0;i < allChildren.length;i++){
    if(allChildren[i] !== node){
      arr[arr.length] = allChildren[i];
      arr.length++;
    }
  }
  return arr;
}
console.log(getSiblings(item3)); 

2. 添加多个 class

操作流程同上,这里只给出代码和必要的解释。

实现功能

  • item1
  • item2
  • item3
  • item4
  • item5
var classes = {'a':true,'b':false,'c':true} for(var key in classes){ var value = classes[key]; if(value){ item3.classList.add(key); } else{ item3.classList.remove(key); } }

利用 hash 存储是否添加的 class ,如果为 true ,添加给 item3 ,如果为 false 移除该 class 。

封装成函数

function addClasses(node,classes){
  for(var key in classes){
    var value = classes[key];
    if(value){
      node.classList.add(key);
    }
    else{
      node.classList.remove(key);
    }
  }
}
addClasses(item3,{'a':true,'b':false,'c':true}); 
// 传入参数包括节点和要添加的 class

优化整理

function addClasses(node,classes){
  for(var key in classes){
    var value = classes[key];
    var methodName = value ? 'add':'remove';
    node.classList[methodName](key);
  }
}
addClasses(item3,{'a':true,'b':false,'c':true});

这里要说明一下,我们根据传来的 key 值为 true 或者 false 来决定是否添加这个 class。node.classList.add(key);node.classList.remove(key); 是属于一类代码,区别就是调用的是 add 方法还是 remove 方法,那么存在优化的可能性。
定义一个 methodName 变量存储 value 值,也就是遍历过程中每次的 true 或者 false。让 node.classList[methodName](key) 每次直接调用 methodName 即可完成操作。
备注:如果你只了解对象 obj.add( ) 这种调用,不理解关于obj[]( ) 调用方法,情回顾 JavaScript 基础。

运行结果

从封装函数到实现简易版自用jQuery(一)_第3张图片
image

2. 建造仓库放工具

现在我们的工具都造好了,就要给工具造房子了。
jQuery 工具的房子叫 jQuery ,那我们也取一个属于自己的名字,比如我的叫 simpleTools 。

window.simpleTools = function(){ 
  return{
    getSiblings:function(){},
    addClass:function(){}
  };
};

window.simpleTools 是我们的大房子,这是一个函数大房子,房子里面现在住着 getSiblings 对象和 addClass 对象,这就好比是放工具的架子。接下来,我们把上面封装好的函数放在对应的架子上。

window.simpleTools = function(node){
 return{
   getSiblings:function(){
    var allChildren = node.parentNode.children;
    var arr = {length:0};
    for(var i = 0;i < allChildren.length;i++){
      if(allChildren[i] !== node){
        arr[arr.length] = allChildren[i];
        arr.length++;
      }
    }
    return arr;
   },
   addClass:function(classes){
     for(var key in classes){
      var value = classes[key];
      var methodName = value ? 'add':'remove';
      node.classList[methodName](key);
     }
   }
 }; 
};

传给 function 的 node 就好像是我们的仓库小管家,一旦他被通知要工作了(有参数传过来),那他就去告诉每一个工具,做好准备随时准备开工。

现在加几句测试语句,看看运行结果。

var nodeTest = simpleTools(item3);
console.log(nodeTest.getSiblings());
nodeTest.addClass({'a':true,'b':false,'c':true});

运行结果

从封装函数到实现简易版自用jQuery(一)_第4张图片
image

从封装函数到实现简易版自用jQuery(一)_第5张图片
image

很好,和之前的运行结果相同,说明我们并没有因为放到仓库里而产生bug。

小结

到这为止,你已经学会了写一个自己的仓库。我们再来回顾一下流程吧。

生产工具:

  1. 实现功能
  2. 封装成函数
  3. 适当优化

建造仓库放工具:

  1. 建一个仓库
  2. 放入工具

快动手实现一个属于自己的仓库吧,
代码的后续优化请看 从封装函数到实现简易版自用jQuery (二)。

你可能感兴趣的:(从封装函数到实现简易版自用jQuery(一))