所謂依賴注入,通俗地舉例,有個人養了一只寵物,他可以喂寵物吃東西,寵物會自己吃:
functionPetKeeper(pet){
this.pet=pet;
}
PetKeeper.prototype.feed=function(food){
this.pet.eat(food);
};
functionPet(type){
this.type=type;
}
Pet.prototype.eat=function(food){
alert("Iama"+this.type+",I'meating"+food);
};
vartom=newPet("cat");
varjerry=newPet("mouse");
varkeeper=newPetKeeper(tom);
keeper.feed("fish");
keeper.pet=jerry;
keeper.feed("rice");
這個例子里,pet是外部注入的,在feed函數定義里,并不知道pet到底是什么(在帶接口的語言里,至少還是知道是個什么,在動態語言里就是兩眼一抹黑了……),只有當它被調用的時候,才知道pet是什么。
這個過程的好處是什么呢?如果我們在PetKeeper內部去創建tom或jerry,就表示PetKeeper要對Pet產生依賴。一個對別人有依賴的東西,它想要單獨測試,就需要在依賴項齊備的情況下進行。如果我們在運行時注入,就可以減少這種依賴,比如在單元測試的時候使用模擬類就行。
比如你有一個a,依賴于b,實際業務中,b的實現很復雜:
functionA(b){
this.b=b;
}
A.prototype.a1=function(){
alert(100+this.b.b1());
};
functionB(){}
B.prototype.b1=function(){
//這里可能很復雜而且不好模擬,比如依賴于生產環境的一些調用
}
那么,我如何用單元測試來驗證A自身的邏輯是正確的呢?如果有強依賴,這里就不好辦了,必須實例化真正的B,但是B的調用要依賴于生產環境。換個方式考慮,我們用一個接口與B相同的類來做模擬,只要改變它的返回值,實現各種邊界條件,把它的實例注入到A的構造函數中,就可以讓A自身的邏輯得到測試了。
functionMockB(){}
MockB.prototype.b1=function(){
return99;
};
在AngularJS里,依賴注入的目的是為了減少組件間的耦合,它的實現是這個過程:
functionArt(Bar,Car){}
我怎么知道這個Art在實例化的時候要傳入Bar和Car的實例呢?形參名稱是沒法取到的,所以只有狠一點,用toString()來取到剛才這一行字符串,然后用正則表達式取到Bar和Car這兩個字符串,然后到模塊映射中取到對應的模塊,實例化之后傳入。
但是這樣也有問題,如果這個js被壓縮了,很可能命名都變了,壓縮成了這樣:
functiona1(b1,b2){}
這時候再這樣就不知道原先是什么類型了。在這里,有類型聲明的語言就不會有問題,比如:
functionart(bar:Bar,car:Car):Art{}
就算你把art,bar,car都改名了,也還是能知道類型,但js里不行。所以,怎么辦呢?
aaa.controller("Art",[function(Bar,Car){},"Bar","Car"]);
注意在AngularJS里面,他很可能建議你這么寫,但也可以這么寫:
Art.$inject=["Bar","Car"];
這么一來,我只要拿到Art,就能取到依賴項的名稱了,就可以實例化再注入,也不怕壓縮了。