本文是对 jQuery 的起源的初步探索。先通过两个函数来扩展原生 DOM 的操作,然后引入命名空间以及对其重构,接着将该命名空间扩大到 Node 上,改造一个自己的 Node2,引出 jQuery。
- <!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>
- <li id="item6">选项6</li>
- </ul>
- </body>
- </html>
此时你刚学完原生 DOM 操作,知道有
。你发现貌似没有直接一下子获得全部兄弟元素的 API 啊,身为一个优秀的 90 后,你果断手写一个函数实现这个需求啊。
- nextSibling previousSibling parentNode
- function getSiblings(node) {
- var allChild = item2.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== node) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- }
好了,以上的函数就能满足需求了, 它接受你传入的某个元素,返回包含这个元素所有兄弟元素的伪数组。
注意: 要用
这样子才不会获得文本节点。所以你想获得 item2 的所有兄弟,只需要 getSiblings(item2)
- item2.parentNode.children
获得所有兄弟的演示地址 ============> demo
领导还没说完,你立马想到了,直接
啊,哈哈,我好聪明啊,不愧是优秀的 90 后。
- item2.classList.add('类名')
给你任意一个元素要直接加上这个类名,别给我的一个一个的加,太二了,如果元素原来有一个不应该存在的类名,给我删了,领导接着说完全部的需求。
这... 看来不能
- item1.classList.add('类名')
- item2.classList.add('类名')
这么弱智的干了啊,那我还用函数嘛,你灵机一动。
- item3.classList.add('类名')
嗯,不愧是善于思考的 90 后
- function addClass(node, classes) {
- for (var key in classes) {
- var value = classes[key]
- if (value) {
- node.classList.add(key)
- } else {
- node.classList.remove(key)
- }
- }
- }
没有使用方法的时候
上图是为添加元素的时候的 item2 的模样,记住它,待会和下图对比。
执行方法后
可以看到,执行方法后,item2 的类名变为 b、c,这是因为你是
这么调用的,意思是类名不应该有 a, 删除 a,并加上 b c。
- addClass(item2, {a: 0, b: 1, c: true})
以上对象的遍历并取值用到了 falsey 值
复习一下,js 的 6 个 falsey 值
除此之外,其他的全是 true。
不过你想的太美了,领导看到你的代码中的这个片段,直接抓狂了……
- if (value) {
- node.classList.add(key)
- } else {
- node.classList.remove(key)
- }
- }
这段代码给我优化了,明明就是一句话的事。
你回去想了一会,可以这么优化
- var methodName = value ? 'add' : 'remove'
- node.classList[methodName](key)
最后你把如下代码提交。
- function addClass(node, classes){
- for (var key in classes){
- var value = classes[key]
- var methodName = value ? 'add' : 'remove'
- node.classList[methodName](key)
- }
- }
- classList['add'] === classList.add
给任一元素添加类名 ==========================> demo
你完成了上面的两个需求后,领导本着锻炼你的原则,又给你提了新的需求。
- var shaolinDom = {} //少林开的超市
- shaolinDom.addClass = addClass //把addClass这个商品收进来
- shaolin.getSibling = getSiblings //把getSiblings这个商品收进来
那我咋用呢,该咋用就咋用呗。
- shaolinDom.addClass(item5, {a: true, b: false, c: 0}) //把item5上原本的b c类名删掉,加上 a类名
- shaolinDom.getSiblings(item6) //获得item6的所有兄弟元素
命名空间
- var shaolinDom = {}
- shaolinDom.addClass = function(node, classes) {
- for (var key in classes) {
- var value = classes[key]
- var methodName = value ? 'add': 'remove'node.classList[methodName](key)
- }
- }
- shaolinDom.getSiblings = function(node) {
- var allChild = node.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== node) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- }
- shaolinDom.addClass(item5, {
- a: true,
- b: false,
- c: 0
- }) var allSiblings = shaolinDom.getSiblings(item6) console.log(allSiblings)
引入命名空间 =======================> demo
命名空间的优化 =====================> demo
- Node.prototype.addClass = function(classes) {
- for (var key in classes) {
- var value = classes[key]
- var methodName = value ? 'add': 'remove'this.classList[methodName](key)
- }
- }
- Node.prototype.getSiblings = function() {
- var allChild = this.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== this) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- }
- item5.addClass({a: true, b: false, c: 0}) //既然Node原型都有了这两函数,item5是node类型,直接用呗
- console.log(item6.getSiblings())
- //上面的代码等同于以下代码
- item5.addClass.call(item5, {a: true, b: false, c: 0}) //call()方法的第一个参数就是this
- console.log(item6.getSiblings.call(item6))
进一步升级,绑定 Node 的原型链上 ==================> demo
用 call() 方便理解 this================================> demo
没多久,领导的考研又来了
可以直接返回一个对象,我也这么干吧
- String() Number() Array()
- window.Node2 = function(node) {
- return {
- getSiblings: function() {
- var allChild = node.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== node) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- },
- addClass: function(classes) {
- for (var key in classes) {
- var value = classes[key]
- var methodName = value ? 'add': 'remove'node.classList[methodName](key) //闭包的使用
- }
- }
- }
- }
- var node2 = Node2(item3) //node2就是用Node2()构造函数构造的返回的对象
- node2.getSiblings() //对象的点运算符去去操作属性啊
- node2.addClass({'a': 0, 'b': true, 'c': true})
自己实现一个构造函数去理解 =======================> demo
- window.jQuery = function(node) {
- return {
- getSiblings: function() {
- var allChild = node.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== node) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- },
- addClass: function(classes) {
- for (var key in classes) {
- var value = classes[key]
- var methodName = value ? 'add': 'remove'node.classList[methodName](key) //闭包的使用
- }
- }
- }
- }
window.$ = jQuery
- window.JQuery = function(nodeOrSelector) {
- let node
- //判断一下nodeOrSelector是node还是一个选择器
- if (typeof nodeOrSelector === 'string') {
- node = document.querySelector(nodeOrSelector)
- } else {
- node = nodeOrSelector
- }
- return {
- getSiblings: function() {
- var allChild = node.parentNode.children
- var childObj = {
- length: 0
- }
- for (let i = 0; i < allChild.length; i++) {
- if (allChild[i] !== node) {
- childObj[childObj.length] = allChild[i] childObj.length += 1
- }
- }
- return childObj
- },
- addClass: function(classes) {
- for (var key in classes) {
- var value = classes[key]
- var methodName = value ? 'add': 'remove'node.classList[methodName](key)
- }
- }
- }
- }
所以
- //var node2 = JQuery('#item3')与下列代码作用相同,把item3变红
- var node2 = JQuery('ul > li:nth-child(3)')
变红啦
jQuery 的雏形 ======================> demo
- querySelectorAll()返回一个NodeList的伪数组
- window.JQuery = function(nodeOrSelector) {
- let nodes = {}
- if (typeof nodeOrSelector === 'string') {
- let temp = document.querySelectorAll(nodeOrSelector) //NodeList
- 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)
- }
- })
- }
- //等同于get、set方法
- 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
- }
控制多个
最终,少林在经理的循循善诱下,开始探索 jQuery 的道路。虽然使用量在下降,但是依然有 60% 的 web 开发人员在用。
以上并不是完全真实的 jQuery 的推导,只是大约是那个意思,可以帮助我更好的理解而已。真正的 JQuery 必须去看文档, 英文文档 , 中文文档
jQuery 我来啦~
来源: http://www.jianshu.com/p/df4e6a18333c