導讀:
本人JS菜鳥一枚,為加強代碼美觀和編程思想。所以來研究下jQuery,有需要進階JS的同學很適合閱讀此文!我是邊看代碼(jquery2.2.1),邊翻“javascript高級程序設計”寫的,有很多基本知識點我都寫了書本對應的章節。有分析得不好的還請各位多多指教,更正!
希望我的分析對大家有所幫助,謝謝!
一、代碼構成
(function(global, factory){ if ( typeof module === "object" && typeof module.exports === "object" ) { //模塊化環境 }else{ factory( global ); } })(typeof window !== "undefined" ? window: this, function(window, noGlobal) { //回調函數 if ( typeof noGlobal === strundefined ) { window.jQuery = window.$ = jQuery; } return jQuery; });
首先一個優美的JS庫都會是在一個匿名函數里去寫自己的代碼對吧,jQuery也不列外。這其實只要把代碼格式化一看,就一目了然。
這個匿名函數接受的是兩個參數,global(當期執行作用域鏈的對象),factory(回調)
匿名函數 : 他本身是做了一個初始化方法的判斷,判斷當前JS使用環境是不是采用的模塊化開發。如果是再做一些相應的邏輯處理(模塊化就不多介紹了,可以自己到網上查詢),否則直接執行回調factory並把當前執行的作用域對象當參數傳遞過去。
回調函數: factory 所有的JQ方法屬性都是在這個回調里實現.里的最后一段代碼,就是對外開放方法的接口。
二、jQuery類的構建
開始入正題了,嘎嘎。。
首先我們來看幾個jQ的一常用場景方法:
$.get(); $.post(); $.extend();//場景一
這么一看jQuery不就是一個Object對象里面添加了幾個方法么,其實確實是的,只不過他是一個Function類型的對象。看下面代碼:
$("#id").html(); $(".class").show();//場景二
是的jQuery就是一個Function對象,那么我們就有幾個問題了:
1、在我們印象中jQuery不是一個類庫么?
2、JS中的類不是用構造函數來仿造的嗎?
3、JS構造函數不是都是用new操作符來實例化的么,為什么jQuery不需要使用new來實例化???
要實現無new操作,我們想到可以使用工廠模式。(工廠模式可以參閱 “高級-第六章 面向對象的程序設計” )接下來我們先看看這個function的代碼:
// Define a local copy of jQuery
jQuery = function(selector, context) { // The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init(selector, context); }
是的他的這段代碼使用的就是JS中典型的工廠模式,工廠模式是不需new操作符的,我們來把jQuery改成一個典型的工廠模式看看;
var jQuery = function(selector,context){ // jQuery("#id")這種使用場景我們留到選擇器的時候再分析
var o = new Object(); o.get = function(){ console.log("get"); }; o.post = function(){ console.log("post"); }; return o; //或者
retrun { get : function(){} ,post : function(){} } //對jQuery中返回一個 new 實例有疑問的參閱(第5章 引用類型)
}; jQuery.get(); //get
jQuery.post(); //post
嗯,如果改成上面這種方式似乎也可以是吧,但我們仔細看下代碼。是不是每次使用我們都需要創建一個新對象,也在內存堆里開辟一個新的內存空間了(堆、棧知識參閱 第4章 變量、作用域和內存問題)那樣就影響性能了,所以我們再改寫下:
var fn = { get : function(){} ,name : "cH" ,post : function(){} ,init : function(){ //這是一個內部構造函數
this.age = 18; console.log(this.age); console.log(this.name); } }; var jQuery = function(selector,context){ return new fn.init(); }; jQuery();
首先解釋下為什么又返回是 return new init 而不是 return fn:
我們如果直接返回fn的話那它就會造成對象引用而且是單實例的。而我們用new實例的話,我們每次使用jQuery都是一個新的實例,這樣與其他的實例他就是沒有任何干擾的。還有就是考慮到$(".calss").hide().html()這種應用場景,我們也必須考慮使用多實例。
那我們每次調用不又是new一個實例,他不是又會造成性影響了么?是的,但是我們把fn拿出來了,所以每次new只是在棧里拷貝了一份這個對象的指針,並不像開始一樣是在堆里新建了一個對象。所以照樣減少了性能開銷。
OK,我們運行下代碼,發現打this.name屬性是undefied,為啥呢?看這個new fn.init(),我們new的init是個實例,在這個實例里的this指向是當前實例的,而name、get這些是fn這個局部變量的,所以this.name當然是undefied咯。那我們再改下代碼:
var fn = { get : function(){ console.log("get"); } ,name : "cH" ,post : function(){} ,init : function(){ //這是一個內部構造函數
this.age = 18; console.log(this.age); console.log(this.name); } }; //我們把內部構造函數的原型繼承fn的屬性 (繼承 第6章 6.2)
fn.init.prototype = fn; var jQuery = function(selector,context){ return new fn.init(); }; jQuery(); jQuery().get();
這下是正常的了,我們把init這個構造函數的原型繼承了fn這個對象。這樣他的屬性就繼承過來了。
我們知道有個這樣的屬性 jQuery.fn
所以再改下代碼:
var jQuery = function(selector,context){ return new jQuery.fn.init(); }; jQuery.fn = { get : function(){ console.log("get"); } ,name : "cH" ,post : function(){} ,init : function(){ //這是一個內部構造函數
this.age = 18; console.log(this.age); console.log(this.name); } }; jQuery.fn.init.prototype = jQuery.fn;
這樣一看代碼就跟源碼差不多了,我們對應再來看下jQuery源碼:
//構建jQuery類 // Define a local copy of jQuery
jQuery = function(selector, context) { // The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init(selector, context); } //定義我們分析時的局部變量
jQuery.fn = jQuery.prototype = { get :... ,name : .... ,post : .... } //定義fn.init方法,內部構造函數
init = jQuery.fn.init = function(selector, context, root) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false)
if (!selector) { return this; } } //內部構造函數的原型繼承
init.prototype = jQuery.fn;
我們發現他定義的局部變量是定義在了jQuery的prototype上了,而fn又引用了他的原型。
1、為啥不用一個單獨的局部變量,而是放在了jQuery的prototype上?
2、fn 他這fn也沒啥特殊意思,就一個引用。感覺這倆步有點多余?
這兩點 望大神解釋!!
三、方法拓展(方法擴展接口)
我們把jQuery的使用方法歸成了倆大類。
//全局方法類
$.get(); $.post(); ... //當前實例類
$("#id").show(); $("#id").css(); ...
全局方法類:
擴展入口:$.extend()
這個擴展就是直接給我們第一的jQuery構造器添加了一些靜態方法
$.extend({meSay : function(){ console.log("meSay") }}); $.meSay();//meSay
//這個擴展就相當於這樣 jQuery.meSay = function...;
當前實例類:
擴展入口:$.fn.extend()
而這個,還記得有個fn的屬性吧,而他的所有屬性方法是被繼承到了當前實例的原型里去了的。所以我們這個擴展就只是給當前實例做的一個擴展。
$.fn.extend({ meSay : function(){ console.log("meSay"); } }); $("#id").meSay();//meSay
//這個擴展相當於 jQuery.fn.meSay = function...;
看到jQuery的源碼,jQuery.extend和jQuery.fn.extend其實是同一個方法的不同引用
jQuery.extend = jQuery.fn.extend = function() {} //jQuery.extend 對jQuery本身的屬性和方法進行了擴展 //jQuery.fn.extend 對jQuery.fn的屬性和方法進行了擴展
extend的實現源碼

1 jQuery.extend = jQuery.fn.extend = function() { 2 var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, 3 i = 1, 4 length = arguments.length, 5 deep = false; 6
7 // Handle a deep copy situation
8 if (typeof target === "boolean") { 9 deep = target; 10
11 // Skip the boolean and the target
12 target = arguments[i] || {}; 13 i++; 14 } 15
16 // Handle case when target is a string or something (possible in deep copy)
17 if (typeof target !== "object" && !jQuery.isFunction(target)) { 18 target = {}; 19 } 20
21 // Extend jQuery itself if only one argument is passed
22 if (i === length) { 23 target = this; 24 i--; 25 } 26
27 for (; i < length; i++) { 28
29 // Only deal with non-null/undefined values
30 if ((options = arguments[i]) != null) { 31
32 // Extend the base object
33 for (name in options) { 34 src = target[name]; 35 copy = options[name]; 36
37 // Prevent never-ending loop
38 if (target === copy) { 39 continue; 40 } 41
42 // Recurse if we're merging plain objects or arrays
43 if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) { 44
45 if (copyIsArray) { 46 copyIsArray = false; 47 clone = src && jQuery.isArray(src) ? src: []; 48
49 } else { 50 clone = src && jQuery.isPlainObject(src) ? src: {}; 51 } 52
53 // Never move original objects, clone them
54 target[name] = jQuery.extend(deep, clone, copy); 55
56 // Don't bring in undefined values
57 } else if (copy !== undefined) { 58 target[name] = copy; 59 } 60 } 61 } 62 } 63
64 // Return the modified object
65 return target; 66 };
這個extend函數就是一個升級的深拷貝函數,jQuery內部的方法屬性也都是使用此方法擴展出來的。我們看下他的使用場景:
$.extend({"meName" : "cHjQuery"}); $.meName // cHjQuery
var d = {name : "d"}; $.extend(d,{age:15}); d //{name :"d",age:15};
var c = $.extend({},{name:"c",age:15}); c //{name :"c",age:15}; //還有第個參數為true的場景,可以查詢jQuery API
jQuery的基本架構就是這樣的了,總結:
1、所有的工作都是在一個匿名函數內執行,保證了命名空間的安全
2、使用了工廠模式構建的類
3、jQuery有一個叫extend的方法,jQuery所有方法屬性都是使用此方法擴展出來的
4、使用的方法分兩大類,全局方法類和實例方法類,他們都有自己的對應的擴展入口
本文為原創文章,轉載請注明出處!
http://www.cnblogs.com/hrw3c/p/5304849.html