深刻剖析js命名空間函數namespace

在看阿里員工寫的開源數據庫鏈接池的druid的源代碼時,發現了其中在jquery的原代碼中又定義了一個命名空間的函數:$.namespace(),其代碼以下:javascript

網址爲:https://github.com/alibaba/druid/blob/master/src/main/resources/support/http/resources/js/jquery.min.jsjava

$.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=window;
        for (j=0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
    return o;
};

使用方法爲:jquery

<script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$.namespace("druid.index");
druid.index=function(){
	var i,j;	// 定義變量
	return {
		login:function(){
			//login 方法的實現
		},
		submit:function(){
			// submit 方法的實現
		}
	};
}();

//使用命名空間的函數
druid.index.login();
druid.index.submit();

 這樣的話,就不會在全局變量區,引入不少的函數,將全部要使用的函數已經變量都放入了命名空間druid.index中,避免了不一樣js庫中的函數名的衝突。git

可是namespace函數的定義如何理解呢?github

$.namespace = function() {  
	   var a=arguments, o=null, i, j, d;  
	   for (i=0; i<a.length; i=i+1) {  
	      d=a[i].split(".");  
	      o=window;  
	      for (j=0; j<d.length; j=j+1) {  
	         o[d[j]]=o[d[j]] || {};  
	         o=o[d[j]];  
	      }  
	   }  
	return o;  
};

 思考了好久,思考的過程明白一個額外的知識點:window這個引用的是不可覆蓋的。好比咱們看下面的代碼:數據庫

console.log(window);
window = {};
console.log(window);
window = null;
console.log(window);
window = undefined;
console.log(window);

 打印的結果都是 window, 而不會是 null 或者 undefined。也就是說window這個名稱,實質上是個引用或者說指針,他指向heap上的全局window對象,stack上的window引用指向heap上的全局window對象,這個指向關係是不可覆蓋,不可修改的。上面我修改了stack上的window,視圖讓他指向Null對象,可是修改是無效的。json

咱們利用firebug來調試看看命名空間究竟是如何實現的,咱們一步一步的接近目標,先看以下代碼:函數

(function(){  
	var o = window;
	console.log(o);	// 打印Window	
	o.druid={};
	console.log(o);	// 打印Window
	console.log(o.druid);	// 打印 Object {}
})(); 

 firebug中顯示的對象爲:ui

上面這個結果應該很好理解,由於 o指向了window,因此o.index = {}; 也就至關於 window.index = {}; 在window上定義了一個名叫index的對象。spa

下面咱們在上面的代碼上加碼,在前進一步,接着看:

(function(){  
	var o = window;
	console.log(o);	// 打印Window	
	o.druid={};
	console.log(o);	// 打印Window
	console.log(o.druid);	// 打印 Object {}

	o = o.druid;
	console.log(o);	// 打印 Object {}
	console.log(window);	// 打印Window
	console.log(o.druid);	// 打印  undefined
})();

 對應firebug中對象和上一步同樣,沒有變化:

上面的代碼中:o = o.druid; 以後,由於 o 是指向 window,爲何console.log(o);  打印 Object {};而 console.log(window); 打印輸出Window呢?這裏的緣由是,沒有理解引用的含義。o 和 window 都是stack上的一個變量,他們都指向heap上的全局window對象,咱們修改 o 這個引用,讓它指向另外的一個空對象,而這並不會同時修改stack上的window這個引用的指向。也就是就像兩條繩子 a, b 都指向一條船,我讓其中的一條繩子b指向第二條船,並不會影響繩子a還指向第一條船。

o = o.druid; 執行以後,o 再也不執行window對象了,而是指向了window.druid對象,那麼最後的console.log(o.druid);爲何打印輸出 undefined 呢?很簡單,由於 o 已經指向了 window.druid; 而window.druid是個空對象,其下並無個druid的屬性,因此天然就打印輸出 undefined 了。

也就是說最後的console.log(o.druid); 就至關於 console.log(window.druid.druid);

 

好,理解了上面的代碼,咱們在加上一段代碼:

(function(){  
	var o = window;
	console.log(o);	// 打印Window	
	o.druid={};
	console.log(o);	// 打印Window
	console.log(o.druid);	// 打印 Object {}

	o = o.druid;
	console.log(o);	// 打印 Object {}
	console.log(window);	// 打印Window
	console.log(o.druid);	// 打印  undefined

	o.index = {};
	console.log(o.index);	// 打印 Object {}
	o = o.index;
	console.log(o.index);	//  undefined
})(); 

 對應的firebug中顯示的對象爲:

咱們看到了已經造成了咱們須要的命名空間:window.druid.index ,其實命名空間是使用對象鏈條來實現的。

由於 o = o.druid; 以後,o 已經指向了 window.druid ,那麼 o.index = {}; 就至關於 window.druid.index = {};

而 後面的 o = o.index; 又將 o 對象變成了一個空對象,再也不指向 window.druid,打印一個空對象的 index 屬性天然就輸出 undefined.

 

到這裏已經就能夠徹底理解namespace函數的定義了。

 

其實核心知識點的有三條:

1)利用了 window 這個特殊引用的不可覆蓋性,不可修改;

2)命名空間實際上是對象鏈條來模擬的;

3)理解引用的含義:引用是個在stack上的變量,能夠修改它指向不一樣的對象,要訪問或者說修改他指向的對象,必須使用 「.」 點操做符,好比 o.index ={}; 而單純的修改 o ,好比 o = {}; 並不會修改他指向的對象,由於 沒有訪問到他指向的對象,怎麼能修改到他指向的對象呢?

 

上面咱們搞明白了$.namespace函數的前因後果,下面咱們看看他的使用如何理解:

$.namespace("druid.index");
druid.index=function(){ var i,j; // 定義變量 return { login:function(){ //login 方法的實現 }, submit:function(){ // submit 方法的實現 } }; }();

首先 $.namespace("druid.index"); 定義了一個命名空間:druid.index,它實際上是 window.druid.index , 而後下面將一個匿名函數的調用的返回值賦值給window.druid.index:

druid.index=function(){
   // ...
}();

而後這個函數返回的是: return {} ,這是什麼?顯然咱們在js中能夠這樣定義一個對象: var obj = {}; 因此這裏返回的是一個js對象,那麼這個js對象的內容是什麼呢:{login:xxx, submit:xxx} 這是什麼??顯然這和咱們的 json 格式是如出一轍的,因此返回的對象是一個json對象,固然也是一個js對象,只不過,該json對象的屬性的值,不是普通的字符串或者json對象,而是一個函數,僅此而已。

相關文章
相關標籤/搜索