为何要学jQuery呢?由于在学习原生js时候,有许多DOM操做冗长复杂,而jQuery却可使之简单化,因此jQuery也曾被众多程序员喜好。那么咱们该怎么去学习jQuery呢?所谓知己知彼百战百胜,咱们能够尝试自制jQuery式的API来了解其原理,在完成这个API后咱们就能很是容易的使用jQuery。html
<!--html代码--> <body> <h1>text</h1> <ul> <li id="item1">选项1</li> <li id="item2">选项2</li> <li id="item3">选项3</li> </ul> </body>
让咱们来添加如下需求:java
以item2为例,为了获取其全部兄弟姐妹,咱们可使用DOM API 中的ParentNode.children属性。但这有一个问题,咱们找到节点父亲后返回的是其全部孩子,为了获得除item2外的全部节点,咱们必须建立一个空对象,把节点父亲返回的全部孩子组成的伪数组遍历后经过条件筛选,将不等于自身节点的其余节点放入此空对象里。node
//javaScript代码 function getSiblings(node) { var allChlidren = node.parentNode.chlidren 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 } //调用方法 getSiblings(item2)
以上代码中,咱们建立的array是一个伪数组,其并无数组的push方法,那么怎么才能把value放进数组呢?因此使用array[array.length]=allChildren[i]的方法(不能放i,由于条件不符合时,会有i跳过了)程序员
咱们能够建立一个classes伪数组,经过遍历后,根据判断其输入的属性值所对应的布尔值来决定使用DOM API中的node.classList.add或者remove来一次性增删该节点的多个类名。面试
//javaScript代码 function addClass(node,classes){ for(let key in classes){ var value = classes[key] var methodName = value? 'add' : 'remove' node.classList[methodName](key) } } // 调用addClass() addClass(item1,{ a:ture , b:false})
在知足以上两点需求后,咱们要是想让别人也来使用咱们这两个函数方法,该怎么办呢?解决办法就是给它添加命名空间。api
//javaScript代码 window.mmmdom = {} mmmdom.getSiblings = function(node) { var allChlidren = node.parentNode.chlidren 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 } mmmdom.addClass = function(node,classes){ for(let key in classes){ var value = classes[key] var methodName = value? 'add' : 'remove' node.classList[methodName](key) } } // 调用方法 mmmdom.getSiblings(item3) mmmdom.addClass( item3,{a:false , b:true })
给它命名空间还有一个好处就是能够避免不知不觉的把全局变量给覆盖。数组
以上咱们已经知足了需求并且还可让别人来使用,可是还有一个问题就是上面的函数调用方法都是textdom.addClass()
这样的,可是按照咱们的习惯,咱们更喜欢使用item2.getClass('red')
这样的调用,接下来咱们就来实现这一点吧。dom
1.扩展node接口
咱们能够直接在 Node.prototype 上添加这个函数,这样咱们的节点就能够继承到这个函数方法了。函数
Node.prototype.getSiblings =function ( ) { var allChlidren = this.parentNode.chlidren 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.call(items) //等价于item3.getSiblings() item3.addClass.call( item1,['text', 'red', 'blue']) //等价于item3.addClass( ['text', 'red', 'blue'])
特别注意:f.call ( asThis , input1 , input2 )
其中this是call()的第一个参数,即asThis会被看成this,而[ input1 ,input2]会被看成arguments学习
可是这样的缺点是,当咱们调用时,若已声明过这个函数,那该函数就会被覆盖了。
2.构造新的接口,即无侵入
//javaScript window.jQuery = function(node){ return{ getSiblings : function(){ var allChlidren = this.parentNode.chlidren 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 }, addClass : function(classes){ classes.forEach( (value) => this.classList.add(value) ) } } } var item3 = jQuery(item3) //实现调用 item3.getSiblings() item3.addClass([' red '])
咱们还能够优化window.$ = jQuery
。
小tips:当一个对象是由 $或jQuery构造出来的,你就在这个对象前也加上$,表示他是jQuery的对象,这样就不会错用成dom api了。即 var $node2 = $(node)。
咱们以上全部操做都是针对节点,那么当传入参数是非节点时该怎么处理呢?咱们应该先对传入参数进行判断:若是传入的是字符串,就用document.querySelectorAll()方法来选择该节点;若是传入的是节点,咱们也把它放入到伪数组中,这是由于querySelectorAll()返回的是伪数组,咱们要保持一致,后续才能使用一样操做把两个方法添加上去。
window.jQuery = function( nodeOrSelector ){ let nodes = {} if( typeof nodeOrSelector === 'string' ){ nodes = document.querySelectorAll(nodeOrSelector) }else if (nodeOrSelector instanceof Node){ nodes = { 0:nodeSelector , length:1 } } nodes.addClass = function (classes) { classes.forEach((value) => { for( let i=0 ; i< nodes.length ; i++ ){ nodes[i].classList.add(value) } }) } return nodes }
这样,咱们就大体理解了jQuery的基本原理了,固然jQuery的做用不单单于此,后续实践会让咱们了解更多的。
<div id=x></div>
var div = document.getElementById('x');
var $div = $('#x');
请说出 div 和 $div 的联系和区别。
div 和 $div 的联系是:$(div) 能够将 div 封装成一个 jQuery 对象,就跟 $div 同样$div[0] === div ,$div 的第一项就是 divdiv 和 $div 的区别是:div 的属性和方法有 childNodes firstChid nodeType 等$div 的 属性和方法有 addClass removeClass toggleClass 等