从封装函数到实现简易版自用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 。
<ul>
  <li id="item1">item1</li>
  <li id="item2">item2</li>
  <li id="item3">item3</li>
  <li id="item4">item4</li>
  <li id="item5">item5</li>
</ul>
  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;
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 元素。

完整代码

<ul>
  <li id="item1">item1</li>
  <li id="item2">item2</li>
  <li id="item3">item3</li>
  <li id="item4">item4</li>
  <li id="item5">item5</li>
</ul>

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

运行结果

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

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

实现功能

<ul>
  <li id="item1">item1</li>
  <li id="item2">item2</li>
  <li id="item3">item3</li>
  <li id="item4">item4</li>
  <li id="item5">item5</li>
</ul>

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 基础。

运行结果

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});

运行结果

image

image

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

小结

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

生产工具:

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

建造仓库放工具:

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock阅读 3,371评论 2 36
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,106评论 0 21
  • 前端开发面试题 <a name='preface'>前言</a> 只看问题点这里 看全部问题和答案点这里 本文由我...
    自you是敏感词阅读 757评论 0 3
  • 今天我观看了开学第一课,于是便有了很多的感想。 一开始,主持人撒贝宁拿了一块纸板,上面写着许多甲骨文。...
    赵怡欣阅读 321评论 0 0
  • 悲喜交加的噩梦,如果说梦到他见到他时,是亲人离去般撕心裂肺的背景,那我宁愿一辈子再也不要梦见遇见他。但愿梦只是梦,...
    CiCi6阅读 202评论 0 0