導讀:javascript
本人JS菜鳥一枚,爲增強代碼美觀和編程思想。因此來研究下jQuery,有須要進階JS的同窗很適合閱讀此文!我是邊看代碼(jquery2.2.1),邊翻「javascript高級程序設計」寫的,有不少基本知識點我都寫了書本對應的章節。有分析得很差的還請各位多多指教,更正!css
但願個人分析對你們有所幫助,謝謝!html
(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方法屬性都是在這個回調裏實現.裏的最後一段代碼,就是對外開放方法的接口。java
開始入正題了,嘎嘎。。
首先咱們來看幾個jQ的一經常使用場景方法:jquery
$.get(); $.post(); $.extend();//場景一
這麼一看jQuery不就是一個Object對象裏面添加了幾個方法麼,其實確實是的,只不過他是一個Function類型的對象。看下面代碼:編程
$("#id").html(); $(".class").show();//場景二
是的jQuery就是一個Function對象,那麼咱們就有幾個問題了:安全
一、在咱們印象中jQuery不是一個類庫麼?
二、JS中的類不是用構造函數來仿造的嗎?
三、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改爲一個典型的工廠模式看看; ide
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又引用了他的原型。
一、爲啥不用一個單獨的局部變量,而是放在了jQuery的prototype上?
二、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的基本架構就是這樣的了,總結:
一、全部的工做都是在一個匿名函數內執行,保證了命名空間的安全
二、使用了工廠模式構建的類
三、jQuery有一個叫extend的方法,jQuery全部方法屬性都是使用此方法擴展出來的
四、使用的方法分兩大類,全局方法類和實例方法類,他們都有本身的對應的擴展入口
本文爲原創文章,轉載請註明出處!