前端基礎進階(11):詳細圖解jQuery物件,以及如何擴充套件jQuery外掛

發表於2017-04-02
前端基礎進階(11):詳細圖解jQuery物件,以及如何擴充套件jQuery外掛
配圖與本文無關

早幾年學習前端,大家都非常熱衷於研究jQuery原始碼。我還記得當初從jQuery原始碼中學到一星半點應用技巧的時候常會有一種發自內心的驚歎,“原來JavaScript居然可以這樣用!”

雖然隨著前端的發展,另外幾種前端框架的崛起,jQuery慢慢變得不再是必須。因此大家對於jQuery的熱情低了很多。但是許多從jQuery中學到的技巧用在實際開發中仍然非常好用。簡單的瞭解它也有助於我們更加深入的理解JavaScript。

這篇文章的主要目的就是跟大家分享一下,jquery物件是如何封裝的。算是對於大家進一步學習jQuery原始碼的一個拋磚引玉。

使用jQuery物件時,我們這樣寫:

在使用之初可能會有許多疑問,比如$是怎麼回事?為什麼不用new就可以直接宣告一個物件等等。後來瞭解之後,才知道原來這正是jQuery物件建立的巧妙之處。

先直接用程式碼展示出來,再用圖跟大家解釋是怎麼回事。

在上面的程式碼中,我封裝了一個簡化版的jQuery物件。它向大家簡單展示了jQuery的整體框架情況。如果瞭解了整體框架,那麼大家去讀jQuery原始碼,就會變得非常輕鬆。

我們在程式碼中可以看到,jQuery自身對於原型的處理使用了一些巧妙的語法,比如jQuery.fn = jQuery.prototypejQuery.fn.init.prototype = jQuery.fn;等,這幾句正式jQuery物件的關鍵所在,下面我用圖給大家展示一下這中間的邏輯是怎麼回事。

前端基礎進階(11):詳細圖解jQuery物件,以及如何擴充套件jQuery外掛
jQuery物件核心圖

物件封裝分析

在上面的實現中,程式碼首先在jQuery建構函式中宣告瞭一個fn屬性,並將其指向了原型jQuery.prototype。並在原型中新增了init方法。

之後又將init的原型,指向了jQuery.prototype。

而在建構函式jQuery中,返回了init的例項物件。

最後對外暴露入口時,將字元$jQuery對等起來。

因此當我們直接使用$('#test')建立一個物件時,實際上是建立了一個init的例項,這裡的正真建構函式是原型中的init方法。

注意:許多對jQuery內部實現不太瞭解的盆友,在使用jQuery時常常會毫無節制使用$(),比如對於同一個元素的不同操作:

通過我們上面的一系列分析,我們知道每當我們執行$()時,就會重新生成一個init的例項物件,因此當我們這樣沒有節制的使用jQuery時是非常不正確的,雖然看上去方便了一些,但是對於記憶體的消耗是非常大的。正確的做法是既然是同一個物件,那麼就用一個變數儲存起來後續使用即可。

擴充套件方法分析

在上面的程式碼實現中,我還簡單實現了兩個擴充套件方法。

要理解它的實現,我們首先要明確的知道this的指向。如果你搞不清楚,可以回頭去看看我們之前關於this指向的講解。傳入的引數options物件為一個key: value模式的物件,我通過for in遍歷options,將key作為jQuery的新屬性,value作為該新屬性所對應的新方法,分別新增到jQuery方法和jQuery.fn中。

也就是說,當我們通過jQuery.extend擴充套件jQuery時,方法被新增到了jQuery建構函式中,而當我們通過jQuery.fn.extend擴充套件jQuery時,方法被新增到了jQuery原型中。

上面的例子中,我也簡單展示了在jQuery內部,許多方法的實現都是通過這兩個擴充套件方法來完成的。

當我們通過上面的知識瞭解了jQuery的大體框架之後,那麼我們對於jQuery的學習就可以具體到諸如css/val/attr等方法是如何實現這樣的程度,那麼原始碼學習起來就會輕鬆很多,也會節約更多的時間。也給一些對於原始碼敬而遠之的朋友提供了一個學習的可能。

有一個朋友留言給我,說她被靜態方法,工具方法和例項方法這幾個概念困擾了很久,到底他們有什麼區別?

其實在上一篇文章中,關於封裝一個物件,我跟大家分享了一個非常非常乾貨,但是卻只有少數幾個讀者老爺get到的知識,那就是在封裝物件時,屬性和方法可以具體放置的三個位置,並且對於這三個位置的不同做了一個詳細的解讀。

而在實現jQuery擴充套件方法的想法中,一部分方法需要擴充套件到jQuery建構函式中,一部分方法需要擴充套件到原型中,當我們通讀jQuery原始碼,還發現有一些方法放在了模組作用域中,至於為什麼會有這樣的區別,建議大家回過頭去讀讀前一篇文章。

而放在建構函式中的方法,因為我們在使用時,不需要宣告一個例項物件就可以直接使用,因此這樣的方法常常被叫做工具方法,或者所謂的靜態方法。工具方法在使用時因為不用建立新的例項,因此相對而言效率會高很多,但是並不節省記憶體。

而工具方法的特性也和工具一詞非常貼近,他們與例項的自身屬性毫無關聯,僅僅只是實現一些通用的功能,我們可以通過$.each$('div').each這2個方法來體會工具方法與例項方法的不同之處。

在實際開發中,我們運用得非常多的一個工具庫就是lodash.js,大家如果時間充裕一定要去學習一下他的使用。

而放在原型中的方法,在使用時必須建立了一個新的例項物件才能訪問,因此這樣的方法叫做例項方法。也正是由於必須建立了一個例項之後才能訪問,所以他的使用成本會比工具方法高很多。但是節省了記憶體。

這樣,那位同學的疑問就很簡單的被搞定了。我們在學習的時候,一定不要過分去糾結一些概念,而要明白具體怎麼回事兒,那麼學習這件事情就不會在一些奇奇怪怪的地方卡住了。

所以通過$.extend擴充套件的方法,其實就是對工具方法的擴充套件,而通過$.fn.extend擴充套件的方法,就是對於例項方法的擴充套件。那麼我們在使用的時候就知道如何準確的去使用自己擴充套件的方法了。

jQuery外掛的實現

我在初級階段的時候,覺得要自己編寫一個jQuery外掛是一件高大上的事情,可望不可即。但是通過對於上面的理解,我就知道擴充套件jQuery外掛其實是一件我們自己也可以完成的事情。

在前面我跟大家分享了jQuery如何實現,以及他們的方法如何擴充套件,並且前一篇文章分享了拖拽物件的具體實現。所以建議大家暫時不要閱讀下去,自己動手嘗試將拖拽擴充套件成為jQuery的一個例項方法,那麼這就是一個jQuery外掛了。

具體也沒有什麼可多說的了,大家看了程式碼就可以明白一切。

後續文章內容一個大概預想

去年年末的時候就有了想要將JavaScript基礎知識總結一下的這樣一個想法,可是JavaScript基礎知識確實並非全部是層層遞進的關係,有很多碎片化的東西,所以之前一直沒有找到一個合適的整理方法。

直到在segmentfault中我在給題主建議如何快速學習一門諸如react/vue這樣的流行框架時,找到了一個好一點的思路,於是就有了這樣一系列文章,雖然它並不全面,很多知識沒有涉及到,但是其實我是圍繞最終通過模組化來構建自己程式碼這樣一個思路來總結的,因此這系列文章能夠解決大家最核心的問題。

也正因為如此,這系列的文章的終點將會是在ES6環境下掌握react的使用。雖然前面我多多少少都涉及到了模組的一些概念,但是還差一個實踐。因此最終我會以ES6的模組跟大家分享如何使用。

那麼後續的文章應該會涉及的內容,就大概包括:

  • 事件迴圈機制
  • Promise
  • ES6的基礎語法
  • ES6下的常用設計模式
  • ES6模組
  • 結合ES6的例項
  • React基礎語法
  • React元件
  • React高階元件
  • React例項
  • Redux

相關文章