首先我們用大白話來理解一次,依賴注入就是我有一個東西,我平時不用,我把這個東西放在你那裡,在我用的時候你拿給我。
這個時候我們已經初步有了概念,我們在用程式碼來消化這個概念
首先我們來先建立一個inject的物件,物件下有個三個屬性分別為
- dependencies這個為一個陣列,用來存放我們所有的依賴
- register 這個為一個函式,用來註冊我們的依賴
- resolve 這個用來注入(提取)我們的依賴
嗯, 大概就是這個,即然已經把初步的想法實現,我們現在就來試試到底可不可以注入const inject = { dependencies: {}, register: function(key, value) { this.dependencies[key] = value; }, resolve: function(deps, func, scope){ let arr = []; for (let i = 0 ; i < deps.length ; i++) { if (this.dependencies.hasOwnProperty(deps[i])) { arr.push(this.dependencies[deps[i]]) } } return function(){ func.apply(scope||{},arr); } }, }複製程式碼
首先實現我有一個東西,並且把這個東西存在你那,那麼我們用register方法來註冊兩個
inject.register('$http', {
'get': function() {
return '我是依賴注入的$http下的一個函式';
}
});
inject.register('$scope', {
'test': ''
});複製程式碼
這個時候我需要用到我前面存放在你那裡東西,你得拿給我
let MyController = function($scope,$http){
console.log(`MyController-result:${$http.get()}`)
}
MyController = inject.resolve(['$scope','$http'],MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼
嗯,完美,這樣就簡單的實現我們的注入,但我們仔細一下,如果這個時候我們把這個依賴注入的順序變換一下是報錯的,因為他們是一一對應的,所以我們還得來改造一下resolve這個方法,
resolve: function(func, scope){
let arr = [];
// 這些正則是在angular的原始碼裡找來的
// 首先把這個函式toString,然後FN_ARGS匹配出來函式的引數,這個引數就是我們依賴的表
// 那麼這個時候我們已經拿到依賴的表了,我們按照這個表裡取出相對應的函式體給他就完了
let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
let fnText = func.toString().replace(STRIP_COMMENTS, '');
let argDecl = fnText.match(FN_ARGS);
let deps = argDecl[1].split(',');
for (let i = 0 ; i < deps.length ; i++) {
if (this.dependencies.hasOwnProperty(deps[i])) {
arr.push(this.dependencies[deps[i]])
}
}
return function(){
func.apply(scope||{},arr);
}
}複製程式碼
當然使用的時候也得改一下
let MyController = function($scope,$http){
console.log(`MyController-result:${$http.get()}`)
}
MyController = inject.resolve(MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼
看上去有點像模樣了,這時候我覺得應該有人會提出問題,嗯,對,就是還有問題,我們知道上線前我們一般會把專案靜態資源打包,function($scope,$http)會打成類似於function(a,b)這種,那麼這個時候還怎麼去打到對應的函式體呢 ? 我們再來改寫一下resolve方法
resolve: function(func, scope) {
let deps;
// 如果傳入的函式是一個陣列,大概長這樣[a,b,function(a,b){}],那麼我們刪掉function(){}這部分,只留下我們的依賴部分,這樣就解決了打包壓縮時的問題
if (isArray(func)) {
let last = func.length - 1;
deps = func.slice(0, last);
func = func[last]
} else {
let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
let fnText = func.toString().replace(STRIP_COMMENTS, '');
let argDecl = fnText.match(FN_ARGS);
deps = argDecl[1].split(',');
}
let arr = [];
for (let i = 0; i < deps.length; i++) {
if (this.dependencies.hasOwnProperty(deps[i])) {
arr.push(this.dependencies[deps[i]])
}
}
return function() {
func.apply(scope || {}, arr);
}
}複製程式碼
當然使用的時候還是得改改
let MyController = ['$scope', '$http', function($scope, $http) {
console.log(`MyController-result:${$http.get()}`)
}];
MyController = inject.resolve(MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼
這樣我們一個簡易的dependencies inject就完成了,angular的實現思路也是如此