現在很多框架都在說攔截器、依賴注入、控制反轉,尤其是java,很多的js框架也引入這種設計思想,包括angular、vue等等,在網上一查也有好多關於這方面的文章,但技術性有很深,但讀過源碼的人應該就明白它核心的原理,由大到小來解釋就是“回調函數”。
1、回調函數
什么是“回調函數”,看下面例子
1 var list = [{a:1,b:2,c:3},{a:3,b:4,c:5},{a:4,b:3,c:2},{a:7,b:1,c:1}]; 2 list.filter(function(item){ 3 return item.a > 3; 4 });
filter是Array的一個函數,正常我們會list.filter()這么調用,來篩選數組,
如果所有的邏輯都寫在filter()內部,我們需要大量的判斷條件,在不清楚list數組內容的情況,就無法去寫filter的條件,
這樣我們就需要把控制權交給外面,里面只提供基礎功能,用於返回新數組。
那我們自己寫一個回調,如下:
1 var filter = function(array,callback){ 2 var _array = []; 3 for(var i in array){ 4 if(callback.call(this,array[i])) _array.push(array[i]); 5 } 6 }
由此能看出,回調函數就是參數只傳入一個函數體,而函數內部執行作為參數的這個函數。
2、攔截器
很多語言都有攔截器,而攔截器的原理和回調函數很像,也是把部分的控制權交給了外部,而內部只是調用包含外部控制權的函數
var c = function(callback){ var b = 0; setInterval(function(){ b += 1; if(b % 3 === 0){ callback.apply(this,[b*1000]); } },1000) } var a = []; c(function(val){ a.push(val); });
a的值: [3000, 6000, 9000, ...] ,會按照每3秒的時間攔截一次,並向a數組中添加
我們在外部可以任意定義callback中的邏輯,如果c函數內部是一個ajax請求或其他異步,我們需要捕捉請求回來時變量被賦值,或者捕捉模塊加載完成時,都可以在需要的節點下調用一個回調函數,並把內部獲取的數據通過參數返回給外部,像:callback.apply(this指向的對象,[參數]),外部可以任意控制處理,這就是攔截器的基本原理。
3、控制反轉和依賴注入
我們也可以用回調來實現,先簡單理解下控制反轉和依賴注入,首先依賴注入是控制反轉的實現,
我們每次實例化對象的時候,都會現new一個新的對象,像var a = new b()
然后用a.xxx的方式來使用b中的屬性和方法,但有一個地方調b的對象,要用到b里y1 : function(),另一個地方也調b對象,卻不能有y1 : function(),
這樣我們就不方便把控制權交給b對象,而是放在外面,而里面只需要讓它自動new一個對象,這就是注入
簡單來說就是外部只需要傳入類的引用,外部就可以直接調用它的對象。
1 export class SummaryComponent implements OnInit, AfterViewInit { 2 constructor(public http: Http){ 3 4 ngOnInit() { 5 //XHR異步請求:發送get請求 6 this.http.get('./app/contact360/summary.sub.component.html').subscribe(data => { 7 alert(data.text()); //如果請求的是文本: data.text() , 如果請求的是json文件: data.json() 8 }); 9 } 10 }
這是angular2注入的例子,在構造方法中傳入Http這個類,正常我需要 this.http = new Http()
但現在我把new的控制權交給了angular,使它在實例化SummaryComponent的時候,順便把構造方法中傳入的類都實例化了,並反給自身,
這樣,我們就可以不用new而直接可以調用this.http的對象,這么做也有個好處就是,new Http()的時候,我不知道傳什么參數給它,而現在我也不用考慮這個了。
問:這和回調函數有什么關系?
答:都是把控制權交給外面,里面只做基本操作
1 define(["demo"],function(_demo){ return new _demo(); });
知道模塊化開發的同學都知道上面這個用於引入新外來模塊用的,但如果define我這么寫
1 _define = function(mods,func){ 2 var _objs = []; 3 define(mods,function(){ 4 for(var i=0;i<arguments.length;i++){ 5 _objs.push(new arguments[i]); 6 } 7 }); 8 func.apply(this,_objs); 9 }
用的時候我就不用new _demo()了,並可以直接用_demo.test()對象方法了
1 _define(["demo"],function(_demo){ return _demo.test(); });
這就是通過回調函數來注入,
當然注入對象的主要還是通過調用接口和繼承的方式,但原理依然脫離不了這種方式,
雖然原理很簡單,但結合各種設計模式后,就會奧妙無窮。