AngularJS之依賴注入模擬實現(xiàn)
一、概述
AngularJS有一經(jīng)典之處就是依賴注入,對于什么是依賴注入,熟悉spring的同學(xué)應(yīng)該都非常了解了,但,對于前端而言,還是比較新穎的。
依賴注入,簡而言之,就是解除硬編碼,達(dá)到解偶的目的。
下面,我們看看AngularJS中常用的實現(xiàn)方式。
方法一:推斷式注入聲明,假定參數(shù)名稱就是依賴的名稱。因此,它會在內(nèi)部調(diào)用函數(shù)對象的toString()方法,分析并提取出函數(shù)參數(shù)列表,然后通過$injector將這些參數(shù)注入進(jìn)對象實例。
如下:
//方法一:推斷式注入聲明,假定參數(shù)名稱就是依賴的名稱。
//因此,它會在內(nèi)部調(diào)用函數(shù)對象的toString()方法,分析并提取出函數(shù)參數(shù)列表,
//然后通過$injector將這些參數(shù)注入進(jìn)對象實例
injector.invoke(function($http, $timeout){
//TODO
});
方法二:行內(nèi)注入聲明,允許我們在函數(shù)定義時,直接傳入一個參數(shù)數(shù)組,數(shù)組包含了字符串和函數(shù),其中,字符串代表依賴名,函數(shù)代表目標(biāo)函數(shù)對象。
如下:
//方法二:行內(nèi)注入聲明,允許我們在函數(shù)定義時,直接傳入一個參數(shù)數(shù)組,
//數(shù)組包含了字符串和函數(shù),其中,字符串代表依賴名,函數(shù)代表目標(biāo)函數(shù)對象。
module.controller('name', ['$http', '$timeout', function($http, $timeout){
//TODO
}]);
看了上述代碼,心中有一疑問,這些是怎么實現(xiàn)的呢?
哈哈,下面,我們就來一起模擬一下這些依賴注入方式,從而了解它們。
二、搭建基本骨架
依賴注入的獲取過程就是,通過字段映射,從而獲取到相應(yīng)的方法。
故而,要實現(xiàn)一個基本的依賴注入,我們需要一個存儲空間(dependencies),存儲所需鍵值(key/value);一個注冊方法(register),用于新增鍵值對到存儲空間中;還有一個就是核心實現(xiàn)方法(resolve),通過相關(guān)參數(shù),到存儲空間中獲得對應(yīng)的映射結(jié)果。
So,基本骨架如下:
var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
}
};
三、完善核心方法resolve
從我們搭建的基本骨架中,可以發(fā)現(xiàn),重點(diǎn)其實resolve方法,用于實現(xiàn)我們具體形式的依賴注入需求。
首先,我們來實現(xiàn),如下形式的依賴注入:推斷式注入聲明。
如下:
injector.resolve(function(Monkey, Dorie){
Monkey();
Dorie();
});
要實現(xiàn)上述效果,我們可以利用函數(shù)的toString()方法,我們可以將函數(shù)轉(zhuǎn)換成字符串,從而通過正則表達(dá)式獲得參數(shù)名,即key值。 然后通過key值,在存儲空間dependencies找value值,沒找到對應(yīng)值,則報錯。
實現(xiàn)如下:
var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var func, deps, args = [], scope = null;
func = arguments[0];
//獲取函數(shù)的參數(shù)名
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];
//獲取函數(shù)的參數(shù)名
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>
推斷式注入聲明,有個缺點(diǎn),就是不能利用壓縮工具壓縮,因為我們是通過函數(shù)的參數(shù)作為依賴的,當(dāng)我們壓縮時,會將參數(shù)名改掉,參數(shù)名都變了,那肯定撲街咯。
那么下面,我們就看看行內(nèi)注入聲明,它可以彌補(bǔ)這缺點(diǎn)。
實現(xiàn)行內(nèi)注入聲明,如下:
injector.resolve(['Monkey', 'Dorie', function(M, D){
M();
D();
}]);
利用typeof判斷arguments[0]的類型可以辨別并獲得依賴參數(shù)和函數(shù)。
實現(xiàn)如下:
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] || {};
//獲得依賴參數(shù)
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;
}
}
//通過依賴參數(shù),找到關(guān)聯(lián)值
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] || {};
//獲得依賴參數(shù)
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;
}
}
//通過依賴參數(shù),找到關(guān)聯(lián)值
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>
因為行內(nèi)注入聲明,是通過字符串的形式作為依賴參數(shù),so,壓縮也不怕咯。
最后,我們將上面實現(xiàn)的兩種方法,整合到一起,就可以為所欲為啦。
那,就合并下吧,如下:
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;
}
}
}
//通過依賴參數(shù),找到關(guān)聯(lián)值
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依賴注入的實現(xiàn),想必,看到這,你自己也會豁然開朗,換湯不換藥嘛。
模擬實現(xiàn)如下:
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>
五、參考
1、AngularJS應(yīng)用開發(fā)思維之3:依賴注入
2、Dependency injection in JavaScript
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
AngularJs Understanding Angular Templates
本文主要介紹AngularJs Understanding Angular Templates的資料,這里整理了詳細(xì)的資料及簡單示例代碼,有興趣的小伙伴的參考下2016-09-09

