实现一个 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)去操作节点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

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

友情链接更多精彩内容