实现一个 jQuery 的 API

HTML 如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
  <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>
</body>
</html>

首先封装两个函数

// 获取除自身以外的所有兄弟节点
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[i],因为 i !== 3,伪数组下标会产生间隔
      array.length += 1
    }
  }
  return array
}
console.log(getSiblings(item3)) // {0: li#item1, 1: li#item2, 2: li#item4, 3: li#item5, length: 4}
// 给一个节点同时加上多个类名
function addClass(node, classes) {
  classes.forEach((value) => node.classList.add(value))
}
addClass(item3, ['a', 'b', 'c'])

命名空间

给这两个函数分别命名,避免出现同名函数互相覆盖的情况

window.zydom = {} // 声明一个全局对象
zydom.getSiblings = function(node) { // zydom.getSiblings 表示全局对象 zydom 中有一个 getSiblings 属性,然后声明一个函数并赋值给它
  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
}
zydom.addClass = function(node, classes) { // zydom.addClass 表示全局对象 zydom 中有一个 addClass 属性,然后声明一个函数并赋值给它
  classes.forEach((value) => node.classList.add(value))
}
// 调用函数
zydom.getSiblings(item3)
zydom.addClass(item3, ['a', 'b', 'c'])

虽然避免了同名函数互相覆盖的情况,但我们觉得zydom.getSiblings(item3)调用函数的写法不够简洁,能不能写成item.getSiblings()这种形式呢?

把 node 放在前面

node.getSiblings()
node.addClass()

方法一:扩展 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() // 相当于 this.getSiblings(),这里的 this 传入的就是 item3
// 等价于 item3.getSiblings.call(item3)
item3.addClass(['a','b','c'])
// 等价于 item3.addClass.call(item3, [abc])

同样地,直接在 Node.prototype 上加函数这种方法也会有同名函数相互覆盖的情况发生,因此采用下面的方法二

方法二:新的接口 BetterNode「无侵入」
给 node 一个新的名字 node2

function Node2(node) {
  return {
    element: node,
    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))
    }
  }
}
let node =document.getElementById('item3')
let node2 = Node2(node) // 函数 Node2() 接受一个 node ,然后返回一个新的对象给 node2
node2.getSiblings() // 这里的 this 是 node2,因此 Node2 函数里不能用 this
node2.addClass(['a','b','c'])

把 Node2 改个名字吧

function jQuery(node) {
  return {
    element: node,
    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))
    }
  }
}
let node =document.getElementById('item3')
let node2 = jQuery(node)
console.log(node2.getSiblings())
node2.addClass(['a','b','c'])

再给它一个缩写 alias

改进一:改掉 document.getElementById
改进二:可以接收多个 node
改进三:添加 .text()

window.$ = jQuery

window.jQuery = function(nodeOrSelector) {
  let nodes = {} // 伪数组
  /*做类型检测*/
  if (typeof nodeOrSelector === 'string') {
    let temp = document.querySelectorAll(nodeOrSelector) // 这里使用临时变量 temp 是为了不让 nodes 返回 NodeList,而是直接返回一个 Object
    for (let i = 0; i < temp.length; i++) {
      nodes[i] = temp[i]
    }
    nodes.length = temp.length
  } else if (nodeOrSelector instanceof Node) {
    nodes = {
      0: nodeOrSelector, // nodes[0] 就表示这个节点
      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) { // getText
      var texts = [] // 数组
      for (let i = 0; i < nodes.length; i++) {
        texts.push(nodes[i].textContent)
      }
    } else { // setText
      for (let i = 0; i < nodes.length; i++) {
        nodes[i].textContent = text
      }
    }
  }
  return nodes // 返回一个伪数组 {0: li, 1: li, length: 5, addClass: f, text: f}
}
window.$ = jQuery
var $nodes = $('ul > li')
$nodes.addClass(['red'])
$nodes.text('hi')

http://js.jirengu.com/qekomedetu/1/edit?html,js,output

总结

jQuery 实质上是一个构造函数,它接受一个参数(可能是节点、选择器、字符串),然后返回一个新的对象(API)去操作节点。

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

推荐阅读更多精彩内容

  • 实现一个jQuery的API功能有: 给获取到的元素增加class 给获取到的元素设置文本 实现思路: 大视角:根...
    xyyojl阅读 394评论 0 1
  • 实现一个jQuery的API 主要实现以下的功能 给元素增加class 给对应的元素设置文本 实现过程 首先声明一...
    qfstudy阅读 191评论 0 1
  • 关于对jQuery学习的帮助可以看方方老师的文章jQuery都过时了,那我还学它干嘛接下来我们自己来实现一个简版的...
    MajorDong阅读 319评论 0 0
  • 2儿子今年三年级下半学期,有点像小大人的感觉了。 我经常把他像大人一样的对待,和他交流思想,经常灌输他自己的事情自...
    花落花开_1168阅读 434评论 1 1
  • 1感恩闹铃可以准时叫我,但我睡了会懒觉 2感恩空调的让我舒服的睡觉 3感恩小火箭的早餐问候 4感恩胡老师分享的财富...
    景烽诚阅读 151评论 0 0