讀一個開源架構,大家最想學到的就是設計的思想和實作的技巧。最近讀jQuery源碼,記下我對大師作品的了解和心得,跟大家分享,權當抛磚引玉。
先附上jQuery的代碼結構。
Js代碼
- (function(){
- //jQuery變量定義
- var jQuery = function(){...};
- //jQuery原型定義(包含核心方法)
- jQuery.fn = jQuery.prototype = {...};
- //看上去很奇怪吧? 非常巧妙的設計,後面詳細介紹
- jQuery.fn.init.prototype = jQuery.fn;
- //提供jQuery靜态方法與對象方法的擴充函數
- jQuery.extend = jQuery.fn.extend = function(){...};
- //後面依次有多個對jQuery靜态方法的擴充
- jQuery.extend({...});
- //後面依次有多個對jQuery對象方法的擴充
- jQuery.fn.extend({...});
- jQuery.support = (function() {...})();
- //提供統一時間管理,jQuery内部使用,并不對外開放
- jQuery.event = {...};
- //Event類似于Java的POJO類.傳遞事件的對象
- jQuery.Event = function( src, props ) {...};
- //Sizzle選擇器,一個架構,可獨立使用。
- (function(){
- ...
- jQuery.find = Sizzle;
- ...
- })();
- ...
- //将定義的jQuery定義為全局變量
- window.jQuery = window.$ = jQuery;
- ...
- })();
(function(){
//jQuery變量定義
var jQuery = function(){...};
//jQuery原型定義(包含核心方法)
jQuery.fn = jQuery.prototype = {...};
//看上去很奇怪吧? 非常巧妙的設計,後面詳細介紹
jQuery.fn.init.prototype = jQuery.fn;
//提供jQuery靜态方法與對象方法的擴充函數
jQuery.extend = jQuery.fn.extend = function(){...};
//後面依次有多個對jQuery靜态方法的擴充
jQuery.extend({...});
//後面依次有多個對jQuery對象方法的擴充
jQuery.fn.extend({...});
jQuery.support = (function() {...})();
//提供統一時間管理,jQuery内部使用,并不對外開放
jQuery.event = {...};
//Event類似于Java的POJO類.傳遞事件的對象
jQuery.Event = function( src, props ) {...};
//Sizzle選擇器,一個架構,可獨立使用。
(function(){
...
jQuery.find = Sizzle;
...
})();
...
//将定義的jQuery定義為全局變量
window.jQuery = window.$ = jQuery;
...
})();
在結構上非常的清晰,定義一個jQuery對象,對jQuery對象進行擴充,賦給window,變成全局變量。就以下幾點做介紹:
1). 自執行的匿名函數。
2). $("...")形式調用傳回 jQuery.fn.init對象。
3). 架構裡最常見的 extend 函數。
一. 自執行匿名函數。
對javascript有一定基礎的都應該知道自執行匿名函數的好處。js是函數作用域。在函數裡定義的變量都是局部變量,這樣就很好的避免了過多的全局變量(jQuery僅僅2個全局變量jQuery和$)。由于閉包屬性,雖然函數自執行結束了,但自執行函數裡面定義的局部函數和變量還是能夠被定義成全局變量的jQuery和$所引用到,類似于Java的私有變量。好處可見一斑。
二. $("...")形式調用傳回 jQuery.fn.init對象。
這是我剛看源碼的時候最不了解的地方。
Js代碼
- var jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- }
- 和
- jQuery.fn.init.prototype = jQuery.fn;
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
}
和
jQuery.fn.init.prototype = jQuery.fn;
看懂這段我們先看看jQuery的使用。jQuery采用鍊式調用(如:$("#id").data("xxx")),這樣就知道$("#id")傳回的是一個jQuery對象。但是調用的方式是函數調用。這個問題就成為了:以函數的方式調用傳回jQuery對象,并且構造函數是init。現在就圍繞解決這個問題展開: 首先想到的是在函數式調用的時候傳回一個jQuery對象。 Js代碼
- var jQuery = function( selector, context ) {
- return new jQuery( selector, context);
- }
var jQuery = function( selector, context ) {
return new jQuery( selector, context);
}
兄弟,你确定這樣? 明眼人一看就知道嚴重的問題所在,死遞歸! 既然不能調用本身,那我們想到另一種辦法:再定義一個函數A,A的原型與jQuery的原型一樣,那A的對象與jQuery生成的對象就是一模一樣了(javascript是原型繼承),而且将A的constructor定義為jQuery,上面的問題不是迎刃而解麼? Js代碼
- var jQuery = function( selector, context ) {
- return new A( selector, context);
- }
- var A = function(){
- if(this.init) {
- this.init();
- }
- };
- A.prototype = jQuery.prototype;
var jQuery = function( selector, context ) {
return new A( selector, context);
}
var A = function(){
if(this.init) {
this.init();
}
};
A.prototype = jQuery.prototype;
這樣就解決了上面的問題,因為jQuery和A擁有同一個原型,是以生成的對象都擁有相同的方法。但是還是感覺A定義的有些多餘,是不是? 既然定義A就為了傳回A的對象,那init函數也能生成對象(以為js中沒有類,定義的函數可以當函數執行,也能new成對象)。既然用init的話,那樣init函數會自動執行,也不用再調用,豈不是更友善!是以就看到了我們之前看到的代碼。 這裡可能還有個fn解釋下,其實這個fn沒有什麼特殊意思,隻是jQuery.prototype的引用,jQuery支援自己擴充屬性,這個對外提供了一個接口,jQuery.fn.extend()來對對象增加方法,比使用jQuery.prototype.extend()更好。封裝想,字面就能看懂是對函數擴充,而不是看上去直接修改prototype.友好的使用者接口。 相對于文字,圖形化更加直覺,對上面的引用來引用去畫了個圖,更好的了解:
其實在使用傳回 new jQuery.fn.init( selector, context, rootjQuery ) 對象方式,我還有 另一種實作: Js代碼
- var jQuery = function( selector, context ) {
- //如果以$("#id") 方式調用this就不是jQuery.這樣傳回jQuery對象
- if(!(this instanceof jQuery)) {
- return new jQuery(selector, context);
- }
- if(this.init) {
- this.init();
- }
- }
- //這行就可以注釋了
- //jQuery.fn.init.prototype = jQuery.fn;
var jQuery = function( selector, context ) {
//如果以$("#id") 方式調用this就不是jQuery.這樣傳回jQuery對象
if(!(this instanceof jQuery)) {
return new jQuery(selector, context);
}
if(this.init) {
this.init();
}
}
//這行就可以注釋了
//jQuery.fn.init.prototype = jQuery.fn;
這種經過測試時可以的,不知道還有沒有其他的隐蔽問題,暫時沒發現,也算是一種實作吧。供大家參考。 三. 架構裡最看到的 extend 函數 在後面的段落中有大段大段的 jQuery.extend({...}) 和 jQuery.fn.extend({...}) 代碼。這裡先解釋下這個的作用和不同。 extend 在java中是繼承,在我之前寫的一篇 <簡單實作Javascrip繼承> 文章不同,也都是用了extend關鍵字。那些我們都說叫繼承,而這裡我更加喜歡叫擴充。為什麼呢? 繼承是産生了新的類,而這裡沒有,這裡的2個函數第一個是擴充jQuery的靜态方法,而第二個是使用者自己擴充對象的方法。靜态方法?對象方法?這裡我來做個解釋,在jQuery中有2中調用形式: 1)$.Ajax(...); 2)$("#id").data("xxx"); 第一種調用我稱為靜态調用,就類似于Java的靜态方法一樣,不用生成對象,而是類級函數。這裡的$就相當于命名空間一樣。我們知道在以往js的程式設計中,如果有命名空間我們都這樣: var ns = {}; ns.Ajax = function(){...}; 那為什麼這裡不是對象,而是函數做一個命名空間呢? 其實在js中一切都是對象!包括函數也是對象(說Java一切都是對象,我覺得其實這句話形容js更加貼切)。 第二種調用我成為對象調用,因為.data()方法是定義在原型中的,隻有new個對象才能調用的,是以成為對象方法。 我們看代碼jQuery.extend = jQuery.fn.extend = function(){...}; 這個是連等,也就是2個指向同一個函數,怎麼會實作不同的功能呢?這就是this 的功能了。jQuery.extend 調用的時候,this是指向jQuery對象的(jQuery是函數,也是對象!),是以這裡擴充在jQuery上。 而jQuery.fn.extend 調用的時候,this指向fn對象,而上圖中科院看到,jQuery.fn 和jQuery.prototype指向同一對象,擴充fn就是擴充jQuery.prototype原型對象。這裡增加的是原型方法,也就是對象方法了。是以jQuery的api中提供了以上2中擴充函數。 本章結束,有不對和不準确的地方望大家指正。有疑問可以留言。
http://www.iteye.com/topic/1126505