jQuery 是什么子眶?
jQuery實質(zhì)上是一個構(gòu)造函數(shù),接受一個參數(shù)架忌,這個參數(shù)可能是節(jié)點,然后返回一個方法對象去操作節(jié)點诺舔。官方文檔是這樣說明的:
jQuery是一個快速,小巧备畦,功能豐富的JavaScript庫低飒。它通過易于使用的API在大量瀏覽器中運行,使得HTML文檔遍歷和操作懂盐,事件處理褥赊,動畫和Ajax變得更加簡單。
那么今天我們就來演示一下jQuery API的工作原理
用原生DOM寫一個類似jQuery的API
1.寫一個帶有id的 ul 列表
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width,
initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>模擬 jQuery API的實現(xiàn)</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>
<script>
</script>
</body>
</html>
2. 以item3為節(jié)點莉恼,找到其兄弟節(jié)點
通過 var allChildren = item3.parentNode.children 獲取 item3 父節(jié)點的所以子節(jié)點拌喉,然后遍歷所有子節(jié)點,得到 item3以外 的所有節(jié)點俐银,這樣就找到選項3的所以兄弟節(jié)點啦尿背。可以 console.log一下捶惜。
(由于array是偽數(shù)組田藐,不能用push的方法,所以我們用到 array[array.length] = allChildren[i] 的方法)
<script>
var allChildren = item3.parentNode.children
var array = {length:0}
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== item3){
array[array.lenth]=allChildren[i]
array.length += 1
}
}
console.log(array)
</script>
3. 代碼封裝
封裝的好處有很多:給代碼一個名字方便調(diào)用吱七;形成局部變量可以避免覆蓋JS原始變量(立即調(diào)用函數(shù))等
給這個函數(shù)取個名字汽久,如 getSiblings;把 item3 換成 node踊餐,這樣輸入任意節(jié)點都可以使用這個函數(shù)了景醇;注意不要忘記 return,這樣我們就得到了一個函數(shù) function getSiblings(node){}
<script>
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.lenth]=allChildren[i]
array.length += 1
}
}
return array
}
</script>
4. 封裝函數(shù):function addClass(node, classes){}
現(xiàn)在我們要給 item3 加 class屬性
首先我們聲明一個 classes 對象吝岭,里面有 a三痰、b、c 三個屬性窜管;同時分別給它們一個布爾值酒觅,方便 add 和 remove;遍歷各個屬性微峰。
var classes = {'a':false, 'b':true, 'c':true}
for(let key in classes){
var value = classes[key]
var methodName = value?'add':'remove';
item3.classList[methodName](key)
}
可以看到舷丹,class b、c已經(jīng)被添加到 item3 中了
同樣我們來封裝一下這些代碼蜓肆,如下所示:
function addClass(node, classes){
for (var key in classes){
var value = classes[key]
var methodName = value ? 'add' : 'remove'
//console.log (methodName )
//console.log (node.classList)
//console.log (node.classList.add)
//console.log (node.classList[methodName])
node.classList[methodName](key)
}
}
/*
obj.x() 等同于 obj['x']()
注意一點上述代碼不能用點運算符颜凯,要用[]運算符谋币,classList['add'] === classList.add
*/
addClass(item3, {a:true, b:false, c:true})
現(xiàn)在,只要你給一個 node 和 classes 于此函數(shù)症概,就可以給 該節(jié)點添加 classes所包含的正確屬性
5.命名空間:
給封裝的函數(shù)一個名字蕾额,方便其他人使用,同時防止與前人命名的沖突彼城。
假如我們想把這兩個封裝好好的函數(shù)聯(lián)系到一起诅蝶,以便以后調(diào)用的話,我們可以這樣去寫募壕。
window.xxdom = {}
xxdom.getSiblings = function (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.length += 1
}
}
return array
}
xxdom.addClass = function (node, classes) {
classes.forEach( (value) => node.classList.add(value) )
}
xxdom.getSiblings(item3)
xxdom.addClass(item3, ['a','b','c'])
6.能不能把node 放在前面
node.getSiblings()
node.addClass()
我們發(fā)現(xiàn)當(dāng)我們要用到上面的命名空間的時候會非常麻煩调炬,我們總是要用 dom.getSiblings()、xxdom.addClass()舱馅、總是要帶有別人的一個小尾巴缰泡。 item3.getSiblings()、item3.addClass() 好像看起來更清爽一些代嗤,有沒有辦法來實現(xiàn)呢棘钞?
其實是有的。為了驗證干毅,我們給 Node 的共有屬性添加 getSiblings屬性宜猜,然后我們測試下能否訪問到:
Node.prototype.getSiblings = function(){
return 1
}
現(xiàn)在有個問題,我們這里的getSiblings()怎么獲取到item3 硝逢?
方法一:擴展 Node 接口
直接在 Node.prototype 上加函數(shù)
Node 如何取到 item3宝恶?
用 this
!
why趴捅?把上面寫成 .call 的形式垫毙,因為this 是call 的第一個參數(shù)。
那么 item3 為什么會有 getSiblings屬性呢拱绑?
因為我們篡改了其 proto 最終指向的 node.prototype 的共有屬性综芥,然后添加了一個 getSiblings的方法,然后它里面呢先去獲取this 的父節(jié)點的所有兒子猎拨,那么 this 是誰呢膀藐?this 就是你在調(diào)用的時候就會幫你把 item3給傳遞進來,item3會自動的傳給 getSiblings() 的 this红省,所以 this 就是.前面的東西额各,不管你是什么。然后聲明一個偽數(shù)組吧恃,遍歷這個偽數(shù)組虾啦,如果偽數(shù)組里面的第[i]項全不等于 this (這時候的 this 就是 item3),那么不等于 item3的 item放到偽數(shù)組里面,偽數(shù)組的 length +1傲醉,循環(huán)后返回這個偽數(shù)組,于是我們得到了 item1硬毕、item2呻引、item4吐咳、item5。
** 自己命名新的接口 Node2**
前面寫的內(nèi)容其實不是很好韭脊,為什么呢童谒?
因為我們在改 node 的共用屬性乾蓬。比如 A 同學(xué)在上面添加了2個函數(shù)慎恒,B 同學(xué)也在上面添加了2個函數(shù)任内,然后我們也添加了2個函數(shù),所以就存在被覆蓋的可能性融柬。
那, 如果不改原型死嗦,我們怎么能實現(xiàn) item3.getSiblings呢?方法也是有的粒氧!就是命名新的接口越除,示例如下:
function Node2(node){
return {
element: node,
getSiblings: function(){
},
addClass: function(){
}
}
}
let node =document.getElementById('x')
let node2 = Node2(node)
node2.getSiblings()
node2.addClass()
真實代碼(這種對 Node 沒有產(chǎn)生破壞的方法叫做「無侵入」)如下:
window.Node2 = function (node) {
return {
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) {
for (var key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}
7. 把 Node2 改成jQuery
通過上面的操作,我們就在原有 Node 的基礎(chǔ)上新擴展了接口外盯,我們這時候就可以給新擴展的起個自己喜歡的名字摘盆,就叫jQuery吧
function jQuery(node) {
return {
element: node,
getSiblings: function () {
},
addClass: function () {
}
}
}
let node = document.getElementById('x')
let node2 = jQuery(node)
node2.getSiblings()
node2.addClass()
實際操作如下:
window.jQuery = function (nodeOrselector) {
let nodes = {}
if (typeof nodeOrselector === 'string') {
let temp = document.querySelectorAll(nodeOrselector) //偽數(shù)組
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
}
nodes.length = temp.length
} else if (nodeOrSelector instanceof node) {
node = {
0: nodeOrSelector,
length: 1
}
}
nodes.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
}
nodes.addClass = function (classes) {
classes.forEach((value) => {
for (let i = 0; i < nodes.length; i++) {
node[i].calssList.add(value)
}
})
}
return nodes
}
window.$ = jQuery //縮寫吧:alias
var $div = $('div')
$div.addClass('red') // 可將所有 div 的 class 添加一個 red
$div.setText('hi') // 可將所有 div 的 textContent 變?yōu)?hi
8. $縮寫與alias
window.$ = jQuery
即var node2 = $(node)
但是為了防止記混 node2 到底有沒有引入 jQuery
大家通常這樣寫:
var $node2 = $(node)
我們會在引用的 jQuery標(biāo)簽前面加上$
符號來區(qū)分原生的標(biāo)簽或者接口,看到$
我們立馬就明白了這是由 jQuery 引用染生出來的饱苟。
到這里我們已經(jīng)知道 jQuery 是個什么了:它就是一個函數(shù)孩擂,是 JS 原始 DOM 的擴展,便于我們更好得使用JS寫代碼的加強版 DOM API箱熬。