摘要:而在構造函數中,返回了的實例對象。在中直接返回過的實例,這里的是的真正構造函數最后對外暴露入口時,將字符與對等起來。因此當我們直接使用創建一個對象時,實際上是創建了一個的實例,這里的正真構造函數是原型中的方法。
早幾年學習前端,大家都非常熱衷于研究jQuery源碼。我還記得當初從jQuery源碼中學到一星半點應用技巧的時候常會有一種發自內心的驚嘆,“原來JavaScript居然可以這樣用!”
雖然隨著前端的發展,另外幾種前端框架的崛起,jQuery慢慢變得不再是必須。因此大家對于jQuery的熱情低了很多。但是許多從jQuery中學到的技巧用在實際開發中仍然非常好用。簡單的了解它也有助于我們更加深入的理解JavaScript。
這篇文章的主要目的就是跟大家分享一下,jquery對象是如何封裝的。算是對于大家進一步學習jQuery源碼的一個拋磚引玉。
使用jQuery對象時,我們這樣寫:
// 聲明一個jQuery對象 $(".target") // 獲取元素的css屬性 $(".target").css("width") // 獲取元素的位置信息 $(".target").offset()
在使用之初可能會有許多疑問,比如$是怎么回事?為什么不用new就可以直接聲明一個對象等等。后來了解之后,才知道原來這正是jQuery對象創建的巧妙之處。
先直接用代碼展示出來,再用圖跟大家解釋是怎么回事。
; (function(ROOT) { // 構造函數 var jQuery = function(selector) { // 在jQuery中直接返回new過的實例,這里的init是jQuery的真正構造函數 return new jQuery.fn.init(selector) } jQuery.fn = jQuery.prototype = { constructor: jQuery, version: "1.0.0", init: function(selector) { // 在jquery中這里有一個復雜的判斷,但是這里我做了簡化 var elem, selector; elem = document.querySelector(selector); this[0] = elem; // 在jquery中返回一個由所有原型屬性方法組成的數組,我們這里簡化,直接返回this即可 // return jQuery.makeArray(selector, this); return this; }, // 在原型上添加一堆方法 toArray: function() {}, get: function() {}, each: function() {}, ready: function() {}, first: function() {}, slice: function() {} // ... ... } jQuery.fn.init.prototype = jQuery.fn; // 實現jQuery的兩種擴展方式 jQuery.extend = jQuery.fn.extend = function(options) { // 在jquery源碼中會根據參數不同進行很多判斷,我們這里就直接走一種方式,所以就不用判斷了 var target = this; var copy; for(name in options) { copy = options[name]; target[name] = copy; } return target; } // jQuery中利用上面實現的擴展機制,添加了許多方法,其中 // 直接添加在構造函數上,被稱為工具方法 jQuery.extend({ isFunction: function() {}, type: function() {}, parseHTML: function() {}, parseJSON: function() {}, ajax: function() {} // ... }) // 添加到原型上 jQuery.fn.extend({ queue: function() {}, promise: function() {}, attr: function() {}, prop: function() {}, addClass: function() {}, removeClass: function() {}, val: function() {}, css: function() {} // ... }) // $符號的由來,實際上它就是jQuery,一個簡化的寫法,在這里我們還可以替換成其他可用字符 ROOT.jQuery = ROOT.$ = jQuery; })(window);
在上面的代碼中,我封裝了一個簡化版的jQuery對象。它向大家簡單展示了jQuery的整體框架情況。如果了解了整體框架,那么大家去讀jQuery源碼,就會變得非常輕松。
我們在代碼中可以看到,jQuery自身對于原型的處理使用了一些巧妙的語法,比如jQuery.fn = jQuery.prototype,jQuery.fn.init.prototype = jQuery.fn;等,這幾句正式jQuery對象的關鍵所在,下面我用圖給大家展示一下這中間的邏輯是怎么回事。
對象封裝分析
在上面的實現中,代碼首先在jQuery構造函數中聲明了一個fn屬性,并將其指向了原型jQuery.prototype。并在原型中添加了init方法。
jQuery.fn = jQuery.prototype = { init: {} }
之后又將init的原型,指向了jQuery.prototype。
jQuery.fn.init.prototype = jQuery.fn;
而在構造函數jQuery中,返回了init的實例對象。
var jQuery = function(selector) { // 在jQuery中直接返回new過的實例,這里的init是jQuery的真正構造函數 return new jQuery.fn.init(selector) }
最后對外暴露入口時,將字符$與jQuery對等起來。
ROOT.jQuery = ROOT.$ = jQuery;
因此當我們直接使用$("#test")創建一個對象時,實際上是創建了一個init的實例,這里的正真構造函數是原型中的init方法。
注意:許多對jQuery內部實現不太了解的盆友,在使用jQuery時常常會毫無節制使用$(),比如對于同一個元素的不同操作:
var width = parseInt($("#test").css("width")); if(width > 20) { $("#test").css("backgroundColor", "red"); }
通過我們上面的一系列分析,我們知道每當我們執行$()時,就會重新生成一個init的實例對象,因此當我們這樣沒有節制的使用jQuery時是非常不正確的,雖然看上去方便了一些,但是對于內存的消耗是非常大的。正確的做法是既然是同一個對象,那么就用一個變量保存起來后續使用即可。
var $test = $("#test"); var width = parseInt($test.css("width")); if(width > 20) { $test.css("backgroundColor", "red"); }
擴展方法分析
在上面的代碼實現中,我還簡單實現了兩個擴展方法。
jQuery.extend = jQuery.fn.extend = function(options) { // 在jquery源碼中會根據參數不同進行很多判斷,我們這里就直接走一種方式,所以就不用判斷了 var target = this; var copy; for(name in options) { copy = options[name]; target[name] = copy; } return target; }
要理解它的實現,我們首先要明確的知道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,大家如果時間充裕一定要去學習一下他的使用。
$.ajax() $.isFunction() $.each() ... ...
而放在原型中的方法,在使用時必須創建了一個新的實例對象才能訪問,因此這樣的方法叫做實例方法。也正是由于必須創建了一個實例之后才能訪問,所以他的使用成本會比工具方法高很多。但是節省了內存。
$("#test").css() $("#test").attr() $("div").each()
這樣,那位同學的疑問就很簡單的被搞定了。我們在學習的時候,一定不要過分去糾結一些概念,而要明白具體怎么回事兒,那么學習這件事情就不會在一些奇奇怪怪的地方卡住了。
所以通過$.extend擴展的方法,其實就是對工具方法的擴展,而通過$.fn.extend擴展的方法,就是對于實例方法的擴展。那么我們在使用的時候就知道如何準確的去使用自己擴展的方法了。
jQuery插件的實現
我在初級階段的時候,覺得要自己編寫一個jQuery插件是一件高大上的事情,可望不可即。但是通過對于上面的理解,我就知道擴展jQuery插件其實是一件我們自己也可以完成的事情。
在前面我跟大家分享了jQuery如何實現,以及他們的方法如何擴展,并且前一篇文章分享了拖拽對象的具體實現。所以建議大家暫時不要閱讀下去,自己動手嘗試將拖拽擴展成為jQuery的一個實例方法,那么這就是一個jQuery插件了。
具體也沒有什么可多說的了,大家看了代碼就可以明白一切。
// Drag對象簡化代碼,完整源碼可在上一篇文章中查看 ; (function() { // 構造 function Drag(selector) {} // 原型 Drag.prototype = { constructor: Drag, init: function() { // 初始時需要做些什么事情 this.setDrag(); }, // 稍作改造,僅用于獲取當前元素的屬性,類似于getName getStyle: function(property) {}, // 用來獲取當前元素的位置信息,注意與之前的不同之處 getPosition: function() {}, // 用來設置當前元素的位置 setPostion: function(pos) {}, // 該方法用來綁定事件 setDrag: function() {} } // 一種對外暴露的方式 window.Drag = Drag; })(); // 通過擴展方法將拖拽擴展為jQuery的一個實例方法 (function ($) { $.fn.extend({ becomeDrag: function () { new Drag(this[0]); return this; // 注意:為了保證jQuery所有的方法都能夠鏈式訪問,每一個方法的最后都需要返回this,即返回jQuery實例 } }) })(jQuery);
后續文章內容一個大概預想
去年年末的時候就有了想要將JavaScript基礎知識總結一下的這樣一個想法,可是JavaScript基礎知識確實并非全部是層層遞進的關系,有很多碎片化的東西,所以之前一直沒有找到一個合適的整理方法。
直到在segmentfault中我在給題主建議如何快速學習一門諸如react/vue這樣的流行框架時,找到了一個好一點的思路,于是就有了這樣一系列文章,雖然它并不全面,很多知識沒有涉及到,但是其實我是圍繞最終通過模塊化來構建自己代碼這樣一個思路來總結的,因此這系列文章能夠解決大家最核心的問題。
這系列文章,算是對于大家學習方向的一個具體的,切實可行的一個指引,而非簡單的通過雞湯的方式告訴你應該如何學習。所以,想要學習這些知識的朋友,趕緊來公眾號關注我吧!!!!
前端基礎進階系列目錄
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/90545.html
摘要:不過其實簡書文章評論里有很多大家的問題以及解答,對于進一步理解文中知識幫助很大的,算是有點可惜吧。不過也希望能夠對正在學習前端的你有一些小幫助。如果在閱讀中發現了一些錯誤,請在評論里告訴我,我會及時更改。 前端基礎進階(一):內存空間詳細圖解 前端基礎進階(二):執行上下文詳細圖解 前端基礎進階(三):變量對象詳解 前端基礎進階(四):詳細圖解作用域鏈與閉包 前端基礎進階(五):全方位...
摘要:一團隊組織網站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術的研究騰訊社交用戶體驗設計,簡稱,騰訊設計團隊網站騰訊用戶研究與體驗設計部百度前端研發部出品淘寶前端團隊用技術為體驗提供無限可能凹凸實驗室京東用戶體驗設計部出品奇舞團奇虎旗下前 一、團隊組織 網站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術的研究 ISUX 騰...
摘要:一團隊組織網站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術的研究騰訊社交用戶體驗設計,簡稱,騰訊設計團隊網站騰訊用戶研究與體驗設計部百度前端研發部出品淘寶前端團隊用技術為體驗提供無限可能凹凸實驗室京東用戶體驗設計部出品奇舞團奇虎旗下前 一、團隊組織 網站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術的研究 ISUX 騰...
摘要:一團隊組織網站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術的研究騰訊社交用戶體驗設計,簡稱,騰訊設計團隊網站騰訊用戶研究與體驗設計部百度前端研發部出品淘寶前端團隊用技術為體驗提供無限可能凹凸實驗室京東用戶體驗設計部出品奇舞團奇虎旗下前 一、團隊組織 網站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術的研究 ISUX 騰...
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現在已經一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現將已經寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
閱讀 3274·2021-11-15 11:37
閱讀 1088·2021-11-02 14:45
閱讀 3905·2021-09-04 16:48
閱讀 3582·2019-08-30 15:55
閱讀 758·2019-08-23 17:53
閱讀 1000·2019-08-23 17:03
閱讀 2035·2019-08-23 16:43
閱讀 2193·2019-08-23 16:22