一、概述 |
AngularJS有一經典之處就是依賴注入,對於什么是依賴注入,熟悉spring的同學應該都非常了解了,但,對於前端而言,還是比較新穎的。
依賴注入,簡而言之,就是解除硬編碼,達到解偶的目的。
下面,我們看看AngularJS中常用的實現方式。
方法一:推斷式注入聲明,假定參數名稱就是依賴的名稱。因此,它會在內部調用函數對象的toString()方法,分析並提取出函數參數列表,然后通過$injector將這些參數注入進對象實例。
如下:
//方法一:推斷式注入聲明,假定參數名稱就是依賴的名稱。 //因此,它會在內部調用函數對象的toString()方法,分析並提取出函數參數列表, //然后通過$injector將這些參數注入進對象實例 injector.invoke(function($http, $timeout){ //TODO });
方法二:行內注入聲明,允許我們在函數定義時,直接傳入一個參數數組,數組包含了字符串和函數,其中,字符串代表依賴名,函數代表目標函數對象。
如下:
//方法二:行內注入聲明,允許我們在函數定義時,直接傳入一個參數數組, //數組包含了字符串和函數,其中,字符串代表依賴名,函數代表目標函數對象。 module.controller('name', ['$http', '$timeout', function($http, $timeout){ //TODO }]);
看了上述代碼,心中有一疑問,這些是怎么實現的呢?
哈哈,下面,我們就來一起模擬一下這些依賴注入方式,從而了解它們。
該篇博客原文地址:http://www.cnblogs.com/giggle/p/5769169.html
二、搭建基本骨架 |
依賴注入的獲取過程就是,通過字段映射,從而獲取到相應的方法。
故而,要實現一個基本的依賴注入,我們需要一個存儲空間(dependencies),存儲所需鍵值(key/value);一個注冊方法(register),用於新增鍵值對到存儲空間中;還有一個就是核心實現方法(resolve),通過相關參數,到存儲空間中獲得對應的映射結果。
So,基本骨架如下:
var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ } };
三、完善核心方法resolve |
從我們搭建的基本骨架中,可以發現,重點其實resolve方法,用於實現我們具體形式的依賴注入需求。
首先,我們來實現,如下形式的依賴注入:推斷式注入聲明。
如下:
injector.resolve(function(Monkey, Dorie){ Monkey(); Dorie(); });
要實現上述效果,我們可以利用函數的toString()方法,我們可以將函數轉換成字符串,從而通過正則表達式獲得參數名,即key值。 然后通過key值,在存儲空間dependencies找value值,沒找到對應值,則報錯。
實現如下:
var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ var func, deps, args = [], scope = null; func = arguments[0]; //獲取函數的參數名 deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(','); scope = arguments[1] || {}; for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t find ' + d); } } func.apply(scope, args); } };
測試代碼,如下:

<!DOCTYPE html> <head> <meta charset="utf-8"/> </head> <body> <script> var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ var func, deps, args = [], scope = null; func = arguments[0]; //獲取函數的參數名 deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(','); scope = arguments[1] || {}; for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t find ' + d); } } func.apply(scope, args); } }; //測試代碼 injector.register('Monkey', function(){ console.log('Monkey'); }).register('Dorie', function(){ console.log('Dorie'); }); injector.resolve(function(Monkey, Dorie){ Monkey(); Dorie(); console.log('-.-'); }); </script> </body> </html>
推斷式注入聲明,有個缺點,就是不能利用壓縮工具壓縮,因為我們是通過函數的參數作為依賴的,當我們壓縮時,會將參數名改掉,參數名都變了,那肯定撲街咯。
那么下面,我們就看看行內注入聲明,它可以彌補這缺點。
實現行內注入聲明,如下:
injector.resolve(['Monkey', 'Dorie', function(M, D){ M(); D(); }]);
利用typeof判斷arguments[0]的類型可以辨別並獲得依賴參數和函數。
實現如下:

var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ var firstParams, func, deps = [], scope = null, args = []; firstParams = arguments[0]; scope = arguments[1] || {}; //獲得依賴參數 for(var i = 0, len = firstParams.length; i < len; i++){ var val = firstParams[i], type = typeof val; if(type === 'string'){ deps.push(val); }else if(type === 'function'){ func = val; } } //通過依賴參數,找到關聯值 for(i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t find ' + d); } } func.apply(scope || {}, args); } };
測試代碼,如下:

<!DOCTYPE html> <head> <meta charset="utf-8"/> </head> <body> <script> var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ var firstParams, func, deps = [], scope = null, args = []; firstParams = arguments[0]; scope = arguments[1] || {}; //獲得依賴參數 for(var i = 0, len = firstParams.length; i < len; i++){ var val = firstParams[i], type = typeof val; if(type === 'string'){ deps.push(val); }else if(type === 'function'){ func = val; } } //通過依賴參數,找到關聯值 for(i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t find ' + d); } } func.apply(scope || {}, args); } }; //測試代碼 injector.register('Monkey', function(){ console.log('Monkey'); }).register('Dorie', function(){ console.log('Dorie'); }); injector.resolve(['Monkey','Dorie',function(M, D){ M(); D(); console.log('-.-'); }]); </script> </body> </html>
因為行內注入聲明,是通過字符串的形式作為依賴參數,so,壓縮也不怕咯。
最后,我們將上面實現的兩種方法,整合到一起,就可以為所欲為啦。
那,就合並下吧,如下:

var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(){ var firstParams, func, deps = [], scope = null, args = []; firstParams = arguments[0]; scope = arguments[1] || {}; //判斷哪種形式的注入 if(typeof firstParams === 'function'){ func = firstParams; deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(','); }else{ for(var i = 0, len = firstParams.length; i < len; i++){ var val = firstParams[i], type = typeof val; if(type === 'string'){ deps.push(val); }else if(type === 'function'){ func = val; } } } //通過依賴參數,找到關聯值 for(i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t find ' + d); } } func.apply(scope || {}, args); } };
四、花絮—RequireJS之依賴注入 |
依賴注入並非在AngularJS中有,倘若你使用過RequireJS,那么下面這種形式,不會陌生吧:
require(['Monkey', 'Dorie'], function(M, D){ //TODO });
通過,上面我們一步步的模擬AngularJS依賴注入的實現,想必,看到這,你自己也會豁然開朗,換湯不換葯嘛。
模擬實現如下:
var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(deps, func, scope){ var args = []; for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t resolve ' + d); } } func.apply(scope || {}, args); } };
測試代碼如下:

<!DOCTYPE html> <head> <meta charset="utf-8"/> </head> <body> <script> var injector = { dependencies: {}, register: function(key, value){ this.dependencies[key] = value; return this; }, resolve: function(deps, func, scope){ var args = []; for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){ if(this.dependencies[d]){ args.push(this.dependencies[d]); }else{ throw new Error('Can\'t resolve ' + d); } } func.apply(scope || {}, args); } }; //測試代碼 injector.register('Monkey', function(){ console.log('Monkey'); }).register('Dorie', function(){ console.log('Dorie'); }); injector.resolve(['Monkey', 'Dorie'], function(M, D){ M(); D(); console.log('-.-'); }); </script> </body> </html>
五、參考 |
2、Dependency injection in JavaScript