jQuery 中的設(shè)計(jì)模式(上)

手寫 DOM 庫(2)朴摊,上一次我們用對象風(fēng)格封裝DOM操作(原生js)丁逝,這次用jQuery風(fēng)格重新封裝钠右。

其它
1.window.jQuery=function(){}
jQuery是全局變量可以直接使用jQuery()
jQuery核心思想
接受一個(gè)selector艾蓝。
根據(jù)這個(gè)選擇器得到一些元素骨望。
return 一個(gè)對象牵囤。
這個(gè)對象有些方法可以操作這個(gè)元素爸黄。
2.舊語法key:value //"addClass":function(參數(shù)){}
ES6新語法 //addClass(參數(shù)) {}
3.聲明一個(gè)對象api,再return這個(gè)對象揭鳞。
其實(shí)可以直接return這個(gè)對象炕贵!
4.當(dāng)變量聲明后只使用一次時(shí),可省略不用聲明野崇。
5.array3 = array1.concat(array2); //concat里是偽數(shù)組
相當(dāng)于array3 = array1 + array2
concat方法創(chuàng)建一個(gè)新的數(shù)組称开,它由被調(diào)用的對象中的元素組成。
將偽數(shù)組變成數(shù)組 Array.from()
6.偽數(shù)組
7.const不能重復(fù)賦值,而且在聲明時(shí)必須賦值
可以用let
8.if (typeof selectorOrArray === 'string') {
...
} else if (selectorOrArray instanceof Array) { //x instanceof object
...
}
對象用instanceof
9.語法 arr.indexOf(searchElement[, fromIndex]) //fromIndex可選鳖轰,示例略
indexOf()方法返回在數(shù)組中可以找到一個(gè)給定元素的第一個(gè)索引清酥。
>=0表示存在,===-1表示不存在蕴侣。

const beasts = ['ant', 'bison', 'camel', 'bison'];
console.log(beasts.indexOf('bison'));   // 1
console.log(beasts.indexOf('giraffe')); // -1

10....展開操作符,可以把一個(gè)數(shù)組展開

...node.children 
等同于
node.children[0],node.children[1],node.children[2]

第二種.用jQuery風(fēng)格重新封裝

完整代碼
鏈?zhǔn)斤L(fēng)格也叫jQuery風(fēng)格
window.jQuery()是我們提供的全局函數(shù)
特殊函數(shù)jQuery
jQuery(選擇器)用于獲取對應(yīng)的元素
但它卻不返回這些元素
相反焰轻,它返回一個(gè)對象,稱為jQuery構(gòu)造出來的對象
這個(gè)對象可以操作對應(yīng)的元素

jQuery核心思想

1.閉包 & 鏈?zhǔn)讲僮?/h3>

添加class

<div class="test">測試1</div>
<div class="test">測試2</div>
<div class="test">測試3</div> 
    
window.jQuery = function (selector) { //接收1個(gè)選擇器#test
    const elements = document.querySelectorAll(selector)
    //api可以操作elements
    const api = {
        addClass(className) {
            for (let i = 0; i < elements.length; i++) {
                elements[i].classList.add(className) //閉包
            }
            return null
        }
    }
    return api //操作elements的api
}

接口
const api = jQuery('.test') //返回api對象
api.addClass('red') //遍歷所有獲取的元素昆雀,添加.red

閉包:函數(shù)訪問外部變量

鏈?zhǔn)讲僮?/h3>

第1步.addClass函數(shù) return api
第2步.api.addClass('red').addClass('blue')

const api = {
  addClass(className) { //主要代碼
    ...
    return api 
    }
 }
 return api
  
接口
const api = jQuery('.test')
api.addClass('red').addClass('blue')//鏈?zhǔn)讲僮?

解析
方法,addClass函數(shù)里return api辱志。
你用api調(diào)了一函數(shù)(addClass),這個(gè)函數(shù)返回前面的那個(gè)東西(api)狞膘。
這樣你就可以繼續(xù)在后面調(diào)addClass揩懒。這種操作就叫鏈?zhǔn)讲僮鳌?/strong>

在這里插入圖片描述

this

obj.fn(p1)
obj.fn.call(obj,p1)
函數(shù)里的this就是obj

api.addClass('red').addClass('blue')
this就是api

addClass(className){
    ...
    return this //api
 }

聲明一個(gè)對象api,再return這個(gè)對象客冈。其實(shí)可以直接return這個(gè)對象旭从!

window.jQuery = function (selector) { 
    const elements = document.querySelectorAll(selector)
    return {
        addClass(className) {
            for (let i = 0; i < elements.length; i++) {
                elements[i].classList.add(className)
            }
            return this
        }
     }
}

總結(jié)
jquery核心思想
1.用閉包維持elements
const api=jQuery('.test')
jQuery提供一個(gè)函數(shù)稳强,這個(gè)函數(shù)接收一個(gè)選擇器(css中的選擇器)场仲。
根據(jù)選擇器獲取到這些元素,但是它不會返回給你這些元素退疫,只會返回一個(gè)對象渠缕,這個(gè)對象會有一些方法(函數(shù)),由函數(shù)操作你的元素。
2.鏈?zhǔn)讲僮鱮eturn this
this是調(diào)用后才確定的!(未知的)
你在addClass前面?zhèn)鞯氖裁窗保瑃his就是什么亦鳞。

const api = jQuery('.test') 
api.addClass('red').addClass('blue')
//this就是api

變量聲明后只用一次時(shí),可以省略聲明
上面的代碼可以簡寫為

jQuery('.test') 
  .addClass('red')
  .addClass('blue')

jQuery對象

var obj=new Object()
Object就是構(gòu)造函數(shù)

jQuery是構(gòu)造函數(shù)嗎棒坏?
是燕差,因?yàn)閖Query函數(shù)確實(shí)構(gòu)造出了一個(gè)對象
不是,因?yàn)椴恍枰獙憂ew jQuery()就能構(gòu)造一個(gè)對象坝冕,以前講的構(gòu)造函數(shù)都要結(jié)合new才行徒探。
結(jié)論
jQuery是一個(gè)不需要加new的構(gòu)造函數(shù)
jQuery不是常規(guī)意義上的構(gòu)造函數(shù)
這是因?yàn)閖Query用了一些技巧(目前沒必要將)

jQuery對象代指jQuery函數(shù)構(gòu)造出來的對象(口頭約定)
只是口頭約定下,jquery是函數(shù)不是普通對象

術(shù)語
舉例
Object是個(gè)函數(shù)喂窟,Object對象表示Object構(gòu)造出的對象
Array是個(gè)函數(shù)测暗,Array對象/數(shù)組對象表示Array構(gòu)造出來的對象
Function是個(gè)函數(shù),F(xiàn)unction對象/函數(shù)對象表示Function構(gòu)造出來的對象

鏈?zhǔn)斤L(fēng)格


1.jQuery('#xxx')返回值不是元素而是一個(gè)api對象
2.jQuery('#xxx').find('.red')查找#xxx里的.red元素
3.實(shí)現(xiàn)end函數(shù)
4.jQuery('.red').each(fn)遍歷并對每個(gè)元素執(zhí)行fn
5.jQuery('#xxx').parent()獲取爸爸
6.jQuery('#xxx').children()獲取兒子

1.略
2.jQuery('#xxx').find('.red')查找#xxx里的.red元素
this是api,閉包變量elements磨澡。

<div class="test"> //elements為3碗啄,3個(gè).test,一個(gè)一個(gè)遍歷
  測試1
   <div class="child">child1</div>
   <div class="child">child2</div>
   <div class="child">child3</div>
</div>
<div class="test">
  測試2
   <div class="child">child1</div>
   <div class="child">child2</div>
</div>
<div class="test">
   測試3
   <div class="child">child1</div>
</div>

find(selector) {
  let arr = []
  for (let i = 0; i < elements.length; i++) {
    const elements2 = Array.from(elements[i].querySelectorAll(selector))
    arr = arr.concat(elements2)
  }
   return arr
}

接口
const x1=jQuery('.test').find('.child')
console.log(x1)

解析:假設(shè)有多個(gè)selector選擇器元素
elements類似于數(shù)組,數(shù)組不能querySelectorAll稳摄。
遍歷elements數(shù)組(當(dāng)前有3個(gè)test數(shù)組)稚字,在數(shù)組里分別find子元素。

在這里插入圖片描述

遍歷到child后厦酬,我們應(yīng)該操作child胆描。如何操作才能確定操作到的是child而不是其它?
當(dāng)前是純數(shù)組arr,返回的也是數(shù)組return arr
數(shù)組不是函數(shù)褒傅,不能直接操作。

Uncaught TypeError: x1.addClass is not a function

那return this可以嗎袄友?不行
this 是當(dāng)前對象'api'殿托,api是操作elements的,它只能操作一個(gè)剧蚣。因此不能操作arr!

接口
jQuery('.test')
    .find('.child')
    .addClass('fuck')
在這里插入圖片描述

return this
return的是find前面的.test而不是我想操作的.child
只能重新封裝一個(gè)jQuery函數(shù)支竹,得到一個(gè)新的api來操作child
jQuery不能只接收選擇器selector還要能接收數(shù)組Array
封裝一個(gè)新的api,操作child鸠按。
之前接收的是selector,現(xiàn)在接收個(gè)數(shù)組礼搁。把數(shù)組給你,然后封裝個(gè)新api
結(jié)構(gòu)一樣目尖,但保存的elements不同
步驟: 第1步馒吴,return由jQuery重新構(gòu)造出來的newApi。(不能直接return之前的api)
第2步瑟曲,jQuery接收選擇器和數(shù)組(selectorOrArray)
第3步饮戳,如果是數(shù)組就等于"新的elements"
const elements在{}內(nèi)作用域有限,可以把它放到外面洞拨,作用域提升扯罐。由于const必須賦值改用let

window.jQuery = function (selectorOrArray) { //第2步
  let elements  //作用域提升
  if (typeof selectorOrArray === 'string') { 
    elements = document.querySelectorAll(selectorOrArray)
  } else if (selectorOrArray instanceof Array) {//第3步
    elements = selectorOrArray
  }
  return {
    addClass(className) {...
     },
      find(selector) {
        let arr = []
        for (let i = 0; i < elements.length; i++) {
        const elements2 = Array.from(elements[i].querySelectorAll(selector))
          arr = arr.concat(elements2)
        }
      // const newApi = jQuery(arr)  
      // return newApi   可以直接簡寫為
      return jQuery(arr)  //第1步,jQuery構(gòu)造出來的newApi
      }
接口
jQuery('.test')
    .find('.child')
    .addClass('fuck')      
在這里插入圖片描述

jQuery構(gòu)造出來的newApi烦衣。
const newApi = jQuery(arr),參數(shù)arr傳入下面jQuery重新生成一個(gè)新arr數(shù)組歹河。
window.jQuery = function (selectorOrArray) {}

3.實(shí)現(xiàn)end函數(shù)

jQuery('.test')
  .find('.child')
  .addClass('red')
  .end() //回到上一次api
  .addClass('fuck') //操作對象是.test而不是.child

用戶突然想回到上一次api操作test,如何實(shí)現(xiàn)?

oldApi: selectorOrArray.oldApi, //把oldApi復(fù)制到當(dāng)前api(之前在數(shù)組上)
find(selector) {
    ...
    arr.oldApi = this //this是舊api
    return jQuery(arr)
 },
end() {
  return this.oldApi //this是新的api2
}
接口
jQuery('.test')
    .find('.child')
    .addClass('red')
    .end()
    .addClass('fuck')

補(bǔ)充:數(shù)組是對象花吟,對象可以加屬性秸歧。

this為什么會變?

幫助理解
const api1 = jQuery('.test')
const api2 = api1.find('.child').addClass('red')
const oldApi = api2.end().addClass('blue')   //當(dāng)前為oldApi

oldApi放到數(shù)組上了并沒有放到api上衅澈,api是操作數(shù)組键菱,this是api而不是arr。
應(yīng)該把oldApi復(fù)制過來
oldApi: selectorOrArray.oldApi,//把oldApi復(fù)制過來,

4.jQuery('.red').each(fn)遍歷并對每個(gè)元素執(zhí)行fn

each(fn) {
  for (let i = 0; i < elements.length; i++) {//elements是閉包,會一直在上面
    fn.call(null, elements[i], i) //不用this
   }
  return this //api對象
}
接口
const x = jQuery('.test').find('.child')
x.each((div) => console.log(div))
在這里插入圖片描述

解析:
each(fn){}接收一個(gè)參數(shù)fn,x調(diào)用each時(shí)傳了個(gè)fn矾麻。
(div)=>console.log(div)就是傳進(jìn)去的參數(shù)fn纱耻。
each遍歷時(shí)會調(diào)用fn,fn在調(diào)用時(shí)傳了兩個(gè)參數(shù)。
fn.call(null,elements[i],i)
elements[i]是第1個(gè)參數(shù)险耀,i是第2個(gè)參數(shù)弄喘。
div就是第1個(gè)參數(shù),名字無所謂不會有任何影響甩牺,只是用來占位的蘑志、形式參數(shù)。
要習(xí)慣在一個(gè)函數(shù)(each)里再傳一個(gè)fn。在這個(gè)fn里拿到這個(gè)參數(shù)(div)急但,這個(gè)參數(shù)實(shí)際上是在調(diào)用fn時(shí)傳給你的,并不是實(shí)際的div澎媒。

5.jQuery('#xxx').parent()獲取爸爸

用each實(shí)現(xiàn)更多的函數(shù)

parent() {
  const array = []
  this.each((node) => {
    if (array.indexOf(node.parentNode) === -1) {//如果存在就不需要push
      array.push(node.parentNode)
     }
   })
 return jQuery(array) //返回可以操作數(shù)組的對象
},
print() {
  console.log(elements)
}
接口
const x = jQuery('.test')
x.parent().print()

return array沒有可操作性,封裝個(gè)操作數(shù)組的對象jQuery(array)波桩,jQuery會返回個(gè)對象戒努,對象會操作這些元素
6.jQuery('#xxx').children()獲取兒子

children() {
  const array = []
  this.each((node) => {
    array.push(...node.children)
   })
  return jQuery(array)
}
接口
const x = jQuery('.test')
x.children().print()

...展開操作符,可以把一個(gè)數(shù)組展開

...node.children
等同于
node.children[0],node.children[1],node.children[2]

實(shí)現(xiàn)createElement、get镐躲、appendTo储玫、append、

window.jQuery = function (selectorOrArrayOrTemplate) {
  let elements
  if (typeof selectorOrArrayOrTemplate === 'string') {
    if (selectorOrArrayOrTemplate[0] === '<') {
      // 創(chuàng)建 div
      elements = [createElement(selectorOrArrayOrTemplate)]
    } else {
      // 查找 div
      elements = document.querySelectorAll(selectorOrArrayOrTemplate)
    }
  } else if (selectorOrArrayOrTemplate instanceof Array) {
    elements = selectorOrArrayOrTemplate
  }

  function createElement(string) {
    const container = document.createElement("template");
    container.innerHTML = string.trim();
    return container.content.firstChild;
  }
  // api 可以操作elements
  return {
    jquery: true,
    elements: elements,
    get(index) {
      return elements[index]
    },
    appendTo(node) {
      if (node instanceof Element) {
        this.each(el => node.appendChild(el)) 
        //遍歷elements萤皂,對每個(gè)el進(jìn)行node.appendChild操作
      } else if (node.jquery === true) {
        this.each(el => node.get(0).appendChild(el)) 
         //遍歷elements撒穷,對每個(gè)el進(jìn)行node.get(0).appendChild(el))操作
      }
    },
    append(children) {
      if (children instanceof Element) {
        this.get(0).appendChild(children)
      } else if (children instanceof HTMLCollection) {
        for (let i = 0; i < children.length; i++) {
          this.get(0).appendChild(children[i])
        }
      } else if (children.jquery === true) {
        children.each(node => this.get(0).appendChild(node))
        }
      },
      ...
  }
}

window.$ = window.jQuery

接口
const $div = $('<div><span>1</span></div>')
const $childList = $('.child')
$('body').append($childList)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市裆熙,隨后出現(xiàn)的幾起案子端礼,更是在濱河造成了極大的恐慌,老刑警劉巖入录,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤奥,死亡現(xiàn)場離奇詭異,居然都是意外死亡纷跛,警方通過查閱死者的電腦和手機(jī)喻括,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贫奠,“玉大人,你說我怎么就攤上這事望蜡』秸福” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵脖律,是天一觀的道長谢肾。 經(jīng)常有香客問我,道長小泉,這世上最難降的妖魔是什么芦疏? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮微姊,結(jié)果婚禮上酸茴,老公的妹妹穿的比我還像新娘。我一直安慰自己兢交,他們只是感情好薪捍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般酪穿。 火紅的嫁衣襯著肌膚如雪凳干。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天被济,我揣著相機(jī)與錄音救赐,去河邊找鬼。 笑死只磷,一個(gè)胖子當(dāng)著我的面吹牛净响,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播喳瓣,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼馋贤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了畏陕?” 一聲冷哼從身側(cè)響起配乓,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惠毁,沒想到半個(gè)月后犹芹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞠绰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年腰埂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜈膨。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屿笼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出翁巍,到底是詐尸還是另有隱情驴一,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布灶壶,位于F島的核電站肝断,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驰凛。R本人自食惡果不足惜胸懈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恰响。 院中可真熱鬧趣钱,春花似錦、人聲如沸渔隶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绞灼,卻和暖如春利术,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背低矮。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工印叁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人军掂。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓轮蜕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蝗锥。 傳聞我的和親對象是個(gè)殘疾皇子跃洛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容