jQuery源碼能夠精簡爲如下內容:javascript
方框上面的代碼根據Jq註釋咱們能夠知道是對AMD規範的支持。css
jQuery總體上被包裹在一個匿名函數中,這個匿名函數再做爲另外一個匿名函數的參數被傳入,形參factory。html
1、自調用函數java
整個部分被自調用函數包裹,經過定義一個匿名函數,建立了一個「私有」的命名空間,該命名空間的變量和方法,不會破壞全局的命名空間。jquery
自調用函數中的第一個參數至關於window,經過傳入window變量,使得window由全局變量變爲局部變量,當在jQuery代碼塊中訪問window時,不須要將做用域鏈回退到頂層做用域,這樣能夠更快的訪問window;這還不是關鍵所在,更重要的是,將window做爲參數傳入,能夠在壓縮代碼時進行優化。編程
第二個參數爲包裹jQuery的匿名函數,在匿名參數中被調用——factory()。api
2、jQuery的無new構建數組
jQuery框架的核心就是從HTML文檔中匹配元素並對其執行操做、瀏覽器
例如:框架
$().find().css() $().hide().html('....').hide().
從上面的寫法上至少能夠發現2個問題
1. jQuery對象的構建方式
2 .jQuery方法的調用方式
分析一:jQuery的無new構建
JavaScript是函數式語言,函數能夠實現類,類就是面向對象編程中最基本的概念
var aQuery = function(selector, context) { //構造函數 } aQuery.prototype = { //原型 name:function(){}, age:function(){} } var a = new aQuery(); a.name();
這是常規的使用方法,顯而易見jQuery不是這樣玩的
jQuery沒有使用new運行符將jQuery顯示的實例化,仍是直接調用其函數
按照jQuery的抒寫方式
$().ready() $().noConflict()
要實現這樣,那麼jQuery就要當作一個類,那麼$()應該是返回類的實例纔對
因此把代碼改一下:
var aQuery = function(selector, context) { return new aQuery(); } aQuery.prototype = { name:function(){}, age:function(){} }
經過new aQuery(),雖然返回的是一個實例,可是也能看出很明顯的問題,死循環了!
那麼如何返回一個正確的實例?
在javascript中實例this只跟原型有關係
那麼能夠把jQuery類看成一個工廠方法來建立實例,把這個方法放到jQuery.prototye原型中
var aQuery = function(selector, context) { return aQuery.prototype.init(); } aQuery.prototype = { init:function(){ return this; } name:function(){}, age:function(){} }
當執行aQuery() 返回的實例:
很明顯aQuery()返回的是aQuery類的實例,那麼在init中的this其實也是指向的aQuery類的實例
問題來了init的this指向的是aQuery類,若是把init函數也看成一個構造器,那麼內部的this要如何處理?
var aQuery = function(selector, context) { return aQuery.prototype.init(); } aQuery.prototype = { init: function() { this.age = 18 return this; }, name: function() {}, age: 20 } aQuery().age //18
這樣的狀況下就出錯了,由於this只是指向aQuery類的,因此須要設計出獨立的做用域才行
jQuery框架分隔做用域的處理
jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); },
很明顯經過實例init函數,每次都構建新的init實例對象,來分隔this,避免交互混淆
那麼既然都不是同一個對象那麼確定又出現一個新的問題
例如:
var aQuery = function(selector, context) { return new aQuery.prototype.init(); } aQuery.prototype = { init: function() { this.age = 18 return this; }, name: function() {}, age: 20 } //Uncaught TypeError: Object [object Object] has no method 'name' console.log(aQuery().name())
拋出錯誤,沒法找到這個方法,因此很明顯new的init跟jquery類的this分離了
怎麼訪問jQuery類原型上的屬性與方法?
作到既能隔離做用域還能使用jQuery原型對象的做用域呢,還能在返回實例中訪問jQuery的原型對象?
實現的關鍵點
// Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn;
經過原型傳遞解決問題,把jQuery的原型傳遞給jQuery.prototype.init.prototype
換句話說jQuery的原型對象覆蓋了init構造器的原型對象
由於是引用傳遞因此不須要擔憂這個循環引用的性能問題
var aQuery = function(selector, context) { return new aQuery.prototype.init(); } aQuery.prototype = { init: function() { return this; }, name: function() { return this.age }, age: 20 } aQuery.prototype.init.prototype = aQuery.prototype; console.log(aQuery().name()) //20
3、鏈式調用
DOM鏈式調用的處理:
1.節約JS代碼.
2.所返回的都是同一個對象,能夠提升代碼的效率
經過簡單擴展原型方法並經過return this的形式來實現跨瀏覽器的鏈式調用。
利用JS下的簡單工廠模式,來將全部對於同一個DOM對象的操做指定同一個實例。
這個原理就超簡單了
aQuery().init().name() 分解 a = aQuery(); a.init() a.name()
把代碼分解一下,很明顯實現鏈式的基本條件就是實例this的存在,而且是同一個
aQuery.prototype = { init: function() { return this; }, name: function() { return this } }
因此咱們在須要鏈式的方法訪問this就能夠了,由於返回當前實例的this,從而又能夠訪問本身的原型了
aQuery.init().name()
優勢:節省代碼量,提升代碼的效率,代碼看起來更優雅
4、插件接口
jQuery支持本身擴展屬性,這個對外提供了一個接口,jQuery.fn.extend()來對對象增長方法
從jQuery的源碼中能夠看到,jQuery.extend和jQuery.fn.extend實際上是同指向同一方法的不一樣引用
jQuery.extend = jQuery.fn.extend = function() {
jQuery.extend 對jQuery自己的屬性和方法進行了擴展,擴展工具方法下的插件 jQuery.fn.extend 對jQuery.fn的屬性和方法進行了擴展,擴展jquery對象下的插件
經過extend()函數能夠方便快速的擴展功能,不會破壞jQuery的原型結構
jQuery.extend = jQuery.fn.extend = function(){...}; 這個是連等,也就是2個指向同一個函數,怎麼會實現不一樣的功能呢?這就是this 力量了!
針對fn與jQuery實際上是2個不一樣的對象,在以前有講述:
- jQuery.extend 調用的時候,this是指向jQuery對象的(jQuery是函數,也是對象!),因此這裏擴展在jQuery上。
- 而jQuery.fn.extend 調用的時候,this指向fn對象,jQuery.fn 和jQuery.prototype指向同一對象,擴展fn就是擴展jQuery.prototype原型對象。
- 這裏增長的是原型方法,也就是對象方法了。因此jQuery的api中提供了以上2中擴展函數。
1 jQuery.extend = jQuery.fn.extend = function() { 2 var src, copyIsArray, copy, name, options, clone, 3 target = arguments[0] || {}, 4 i = 1, 5 length = arguments.length, 6 deep = false; 7 8 // Handle a deep copy situation 9 //判斷第一個參數是否爲boolean類型,也就是是否深度嵌套對象 10 if ( typeof target === "boolean" ) { 11 deep = target; 12 13 // skip the boolean and the target 14 //跳過第一個boolean參數 15 target = arguments[ i ] || {}; 16 i++; 17 } 18 19 // Handle case when target is a string or something (possible in deep copy) 20 // 處理奇怪的狀況,好比 jQuery.extend( 'hello' , {nick: 'casper}) 21 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 22 target = {}; 23 } 24 25 // extend jQuery itself if only one argument is passed 26 if ( i === length ) { 27 target = this; 28 i--; 29 } 30 31 for ( ; i < length; i++ ) { 32 // Only deal with non-null/undefined values 33 if ( (options = arguments[ i ]) != null ) {//opitions爲一個個參數對象 34 // Extend the base object 35 for ( name in options ) { 36 src = target[ name ]; 37 copy = options[ name ]; 38 39 // Prevent never-ending loop 40 //防止自引用 41 if ( target === copy ) { 42 continue; 43 } 44 45 // Recurse if we're merging plain objects or arrays 46 //深拷貝 47 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 48 if ( copyIsArray ) {//被拷貝屬性值是個數組 49 copyIsArray = false; 50 clone = src && jQuery.isArray(src) ? src : []; 51 52 } else { 53 clone = src && jQuery.isPlainObject(src) ? src : {}; 54 } 55 56 // Never move original objects, clone them 57 //遞歸拷貝 58 target[ name ] = jQuery.extend( deep, clone, copy ); 59 60 // Don't bring in undefined values 61 } else if ( copy !== undefined ) {//淺拷貝,屬性值不是自定義 62 target[ name ] = copy; 63 } 64 } 65 } 66 } 67 68 // Return the modified object 69 return target; 70 };