淺談 jQuery 核心架構設計
jQuery對於大家而言並不陌生,因此關於它是什麼以及它的作用,在這裡我就不多言了,而本篇文章的目的是想透過對源碼簡單的分析來討論jQuery 的核心架構設計,以及jQuery 是如何利用javascript中的高階特性來建立如此偉大的javascript函式庫。
1 初識jQuery
從核心功能來看,jQuery僅僅做了一件簡單而又平凡的事:查詢。它的文法如此簡潔明了,以致於很多人在不知道javascript是什麼的時候就已經會用jQuery了,用一個字形容就是:大道至簡。 從設計層面來看,我們可以將jQuery提供方法分為兩大類:靜態方法和實例方法。靜態方法就是直接透過$存取的方法,這些方法一般不對dom元素操作,而是提供了一些常用的工具,例如ajax請求、以及對字串的一些常用操作,除此之外,jQuery還提供了對自身的擴充機制,你可以透過extend方法來寫你需要的元件。而實例方法和靜態方法不一樣,它是用來對jQuery查詢的DOM元素進行操作,jQuery執行$()會建構一個jQuery對象,這個對像以數組的方法存儲查詢出的所有DOM元素,然後在這個在物件的原型鏈上實作了對這些DOM操作的方法,例如each()方法就是用來遍歷每一個DOM元素的。你可能會注意到,我只是說這個物件「以數組的方式」存儲,那就是說,jQuery建構的這個物件不是數組,那這個物件到底是什麼? 其實這個物件就是jQuery的核心,也被稱為「jQuery物件」。因此,本文的重點就是對jQuery物件進行分析與討論。
2 jQuery物件
一般情況下,我們會這樣使用jQuery:
$('div').each(function(index){ //this ...});
$('div')執行完後回傳一個jQuery物件,each()是對這個物件中的方法是對這個物件中的方法元素進行遍歷,我們先來看看$('div')的執行過程(本文源碼摘自jQuery 3.0):
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }
這個方法就是$('div')的入口方法,$是jQuery的簡寫,就相當於jQuery('div') ,可以看出,這個方法只做了一件事,那就是返回jQuery.fn.init()函數的實例對象,那jQuery.fn.init 又是什麼呢,我們再看下面的程式碼:
init = jQuery.fn.init = = jQuery.fn;
jQuery.fn.init和init引用了同一個方法,這個方法根據selector查詢出符合條件的DOM元素,並返回,可你會發現,返回的是this,這個this是什麼呢?我們待會分析,先看下面的這句話:
init.prototype = jQuery.fn;
這句話是什麼意思呢,這句話是讓init方法的prototype對象指向了jQuery.fn對象,那jQuery.fn又是什麼鬼? 我們繼續看程式碼:
jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // The default length of a jQuery object is 0 length: 0, // Execute a callback for every element in the matched set. each: function( callback ) { return jQuery.each( this, callback ); }, splice: arr.splice };
為了節省篇幅,我省略了其中一些程式碼,從這裡可以看出,jQuery.fn 其實就是jQuery的原型對象,這個原型物件中定義了一些對this對象進行操作的方法。到這裡,你是不是感覺到有點繞,不要著急,我們來梳理一下思路:jQuery首先定義了一個init方法,然後在init的原型對象prototype上定義了一系列操作方法。最後將init方法的實例物件傳回。所以上面的過程可以簡化如下(偽代碼表示):
var init = function(selector,context,root){ //... return this; } init.prototype = { length:0, each:function(callback){ //... }, splice:[].splice } jQuery = function(selector,context,root){ return new init(selector,context,root); }
那麼問題來了,jQuery.fn中的方法為什麼不直接定義在init的prototype上,而要定義在jQuery的原型對像上?
其實,這樣做的目的是為了提高jQuery的查詢效率,如果直接定義在init的prototype對像上,那麼每執行一次查詢,就會在內存中創建這樣一個龐大的prototype對象,而如果把這個對象定義在jQuery的prototype上,在jQuery載入時,這個物件就會被初始化並且一直存在於記憶體中,以後每次執行$()時,只需要將init中的prototype指向這個物件就可以了,而不用每次都去創建一遍相同的物件。
我們再來看看 init 函數中回傳的 this 到底是什麼,我在之前的部落格中講過,函數中的this總是指向運行期的呼叫者,那init的呼叫者是誰呢?在上面程式碼中似乎找不到呼叫者,這時我們就需要深入的理解new運算子的運作機制了,借用我之前在部落格中對new運算子的描述,我們對new init()的執行過程進行如下分解:
new init(selector,context,root) = { var obj = {}; obj.__proto__ = init.prototype; init.call(obj,selector,context,root); return typeof result === 'obj'? result : obj; }
從上述分解過程可以看出,javascript在透過new 來創建一個實例對象的時候,會先創建了一個普通對象obj,然後將obj的內部屬性__proto__指向了init的原型對象,因此obj的原型鏈將被改變,而第3步使用call方法呼叫init(),所以init中的this指的就是這裡的obj物件。
init()執行以後,會將匹配到的所有DOM對像以數組的方式存儲到this對像中並返回,也就是返回了obj對象,而new運算符最終也會將這個obj 對象返回以作為新的實例物件。 所以new運算子回傳的這個實例物件具備兩個特點:一是包含了DOM查詢結果集,二是其原型鏈繼承了init的prototype,而init 的prototype 又指向了jQuery.fn對象,因此實例對象也具備了這些操作方法。
jQuery每執行一次查詢就會建立一個jQuery對象,而在同一個應用程式中,所有jQuery物件都會共用同一個jQuery原型物件。因此,jQuery物件不僅包含了DOM查詢結果集,也繼承了jQuery原型物件上的操作方法。這樣,你就可以在查詢後直接呼叫方法來操作這些DOM元素了。這就是jQuery的核心架構設計,簡單、方便、實用!
如果你還不理解上面的講解,不要著急,我按照jQuery的設計思路寫了一個完整的小項目jDate,你可以對比著理解! jDate專案已上傳至GitHub,你可以點擊這裡查看完整程式碼:jDate ,如有不同見解,歡迎討論!
3 jQuery 的缺陷
透過對jQuery的核心架構分析,我們會發現,每執行一次查詢,jQuery就要在內存中構建一個複雜的jQuery對象,雖然說每個jQuery對像都共享同一個jQuery原型,但jQuery的查詢過程遠比你想像的要複雜,它既要考慮各種不同的匹配標識,同時又要考慮不同瀏覽器的兼容性。因此,如果你只是對DOM做一些簡單的操作,建議使用原生方法querySelector 替代jQuery,不過在使用原生方法時,對於不同的應用場景你可能要做一些兼容性的工作,你要學會取捨,不要過度依賴jQuery!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

隨著物聯網和雲端運算的快速發展,邊緣運算逐漸成為新的熱點。邊緣運算是指將資料處理和運算能力從傳統的雲端運算中心轉移到實體設備的邊緣節點上,以提高資料處理的效率和減少延遲。而MongoDB作為一種強大的NoSQL資料庫,其在邊緣運算領域的應用也越來越受到重視。一、MongoDB與邊緣運算的結合實務在邊緣運算中,設備通常具有有限的運算與儲存資源。而MongoDB

GolangRabbitMQ:實現高可用的訊息佇列系統的架構設計和實現,需要具體程式碼範例引言:隨著網路技術的不斷發展和應用的廣泛,訊息佇列成為了現代軟體系統中不可或缺的一部分。作為實現解耦、非同步通訊、容錯處理等功能的工具,訊息佇列為分散式系統提供了高可用性和擴充性的支援。而Golang作為一種高效、簡潔的程式語言,廣泛應用於建構高並發和高效能的系統

隨著網路的快速發展,軟體開發變得越來越複雜。為了因應這項挑戰,軟體架構也不斷演進,從最初的單體應用,到微服務架構。而隨著微服務架構的普及,越來越多的開發者開始採用gRPC作為微服務之間的通訊協定。 go-zero就是一套基於gRPC建構的微服務架構。本文將介紹go-zero的架構設計模式及最佳實務。一、go-zero框架架構圖1:go-zero框架架構如圖1

商城SKU管理模組的架構設計與PHP程式碼實現一、引言隨著電子商務的快速發展,商城的規模與複雜性也日益增加。商城的SKU(StockKeepingUnit)管理模組是商城的核心模組之一,負責管理商品的庫存、價格、物業等資訊。本文將介紹商城SKU管理模組的架構設計與PHP程式碼實作。二、架構設計資料庫設計SKU管理模組的資料庫設計是整個架構的基礎。商城的SKU

大型PHP專案可採用基於框架的架構設計,如分層架構或MVC架構,以實現可擴充性、可維護性和可測試性。分層架構包括視圖層、業務邏輯層和資料存取層;MVC架構將應用程式劃分為模型、視圖和控制器。實施框架架構可提供模組化設計,方便添加新功能、降低維護成本並支援單元測試。

作為一款高效能的程式語言,Go語言在分散式系統的建置中非常流行。它的高速度和極低的延遲時間讓開發人員更容易實現高擴展性的分散式架構。在建構分散式系統前,需考慮的架構問題非常繁瑣。如何設計出更易於維護、可擴展且穩定的架構是所有分散式系統開發者面臨的重要問題。使用Go語言來建立分散式系統,可以使這些架構選擇變得更加簡單和明確。高效率的協程Go語言天生支持協程,

如何設計一個高效能的PHP微服務架構隨著網際網路的快速發展,微服務架構成為了許多企業建構高效能應用的首選。作為一種輕量級、模組化的架構風格,微服務可以將複雜的應用拆分成更小的、獨立的服務單元,透過相互協作提供更好的擴展性、可靠性和可維護性。本文將介紹如何設計一個高效能的PHP微服務架構,並提供了具體的程式碼範例。一、拆分微服務在設計高效能的PHP微服務架構前,

網站安全架構設計指南:PHP防火牆的實現引言:在現今的互聯網時代,網站安全問題日益嚴峻,不斷有駭客利用漏洞入侵網站,盜取用戶資訊或破壞網站正常運作。為了保護網站及其使用者的隱私和安全,建立一個可靠的安全架構是至關重要的。本文將重點放在PHP防火牆的實現,為網站安全架構提供指導。一、什麼是PHP防火牆? PHP防火牆是一種屏蔽惡意攻擊和入侵的安全措施,它透過過濾
