AngularJS之依賴注入模擬實(shí)現(xiàn)
一、概述
AngularJS有一經(jīng)典之處就是依賴注入,對(duì)于什么是依賴注入,熟悉spring的同學(xué)應(yīng)該都非常了解了,但,對(duì)于前端而言,還是比較新穎的。
依賴注入,簡(jiǎn)而言之,就是解除硬編碼,達(dá)到解偶的目的。
下面,我們看看AngularJS中常用的實(shí)現(xiàn)方式。
方法一:推斷式注入聲明,假定參數(shù)名稱就是依賴的名稱。因此,它會(huì)在內(nèi)部調(diào)用函數(shù)對(duì)象的toString()方法,分析并提取出函數(shù)參數(shù)列表,然后通過(guò)$injector將這些參數(shù)注入進(jìn)對(duì)象實(shí)例。
如下:
//方法一:推斷式注入聲明,假定參數(shù)名稱就是依賴的名稱。 //因此,它會(huì)在內(nèi)部調(diào)用函數(shù)對(duì)象的toString()方法,分析并提取出函數(shù)參數(shù)列表, //然后通過(guò)$injector將這些參數(shù)注入進(jìn)對(duì)象實(shí)例 injector.invoke(function($http, $timeout){ //TODO });
方法二:行內(nèi)注入聲明,允許我們?cè)诤瘮?shù)定義時(shí),直接傳入一個(gè)參數(shù)數(shù)組,數(shù)組包含了字符串和函數(shù),其中,字符串代表依賴名,函數(shù)代表目標(biāo)函數(shù)對(duì)象。
如下:
//方法二:行內(nèi)注入聲明,允許我們?cè)诤瘮?shù)定義時(shí),直接傳入一個(gè)參數(shù)數(shù)組, //數(shù)組包含了字符串和函數(shù),其中,字符串代表依賴名,函數(shù)代表目標(biāo)函數(shù)對(duì)象。 module.controller('name', ['$http', '$timeout', function($http, $timeout){ //TODO }]);
看了上述代碼,心中有一疑問(wèn),這些是怎么實(shí)現(xiàn)的呢?
哈哈,下面,我們就來(lái)一起模擬一下這些依賴注入方式,從而了解它們。
二、搭建基本骨架
依賴注入的獲取過(guò)程就是,通過(guò)字段映射,從而獲取到相應(yīng)的方法。
故而,要實(shí)現(xiàn)一個(gè)基本的依賴注入,我們需要一個(gè)存儲(chǔ)空間(dependencies),存儲(chǔ)所需鍵值(key/value);一個(gè)注冊(cè)方法(register),用于新增鍵值對(duì)到存儲(chǔ)空間中;還有一個(gè)就是核心實(shí)現(xiàn)方法(resolve),通過(guò)相關(guān)參數(shù),到存儲(chǔ)空間中獲得對(duì)應(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)其實(shí)resolve方法,用于實(shí)現(xiàn)我們具體形式的依賴注入需求。
首先,我們來(lái)實(shí)現(xiàn),如下形式的依賴注入:推斷式注入聲明。
如下:
injector.resolve(function(Monkey, Dorie){ Monkey(); Dorie(); });
要實(shí)現(xiàn)上述效果,我們可以利用函數(shù)的toString()方法,我們可以將函數(shù)轉(zhuǎn)換成字符串,從而通過(guò)正則表達(dá)式獲得參數(shù)名,即key值。 然后通過(guò)key值,在存儲(chǔ)空間dependencies找value值,沒(méi)找到對(duì)應(yīng)值,則報(bào)錯(cuò)。
實(shí)現(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); } };
測(cè)試代碼,如下:
<!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); } }; //測(cè)試代碼 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>
推斷式注入聲明,有個(gè)缺點(diǎn),就是不能利用壓縮工具壓縮,因?yàn)槲覀兪峭ㄟ^(guò)函數(shù)的參數(shù)作為依賴的,當(dāng)我們壓縮時(shí),會(huì)將參數(shù)名改掉,參數(shù)名都變了,那肯定撲街咯。
那么下面,我們就看看行內(nèi)注入聲明,它可以彌補(bǔ)這缺點(diǎn)。
實(shí)現(xiàn)行內(nèi)注入聲明,如下:
injector.resolve(['Monkey', 'Dorie', function(M, D){ M(); D(); }]);
利用typeof判斷arguments[0]的類型可以辨別并獲得依賴參數(shù)和函數(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; } } //通過(guò)依賴參數(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); } };
測(cè)試代碼,如下:
<!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; } } //通過(guò)依賴參數(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); } }; //測(cè)試代碼 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>
因?yàn)樾袃?nèi)注入聲明,是通過(guò)字符串的形式作為依賴參數(shù),so,壓縮也不怕咯。
最后,我們將上面實(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] || {}; //判斷哪種形式的注入 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; } } } //通過(guò)依賴參數(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中有,倘若你使用過(guò)RequireJS,那么下面這種形式,不會(huì)陌生吧:
require(['Monkey', 'Dorie'], function(M, D){ //TODO });
通過(guò),上面我們一步步的模擬AngularJS依賴注入的實(shí)現(xiàn),想必,看到這,你自己也會(huì)豁然開(kāi)朗,換湯不換藥嘛。
模擬實(shí)現(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); } };
測(cè)試代碼如下:
<!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); } }; //測(cè)試代碼 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)用開(kāi)發(fā)思維之3:依賴注入
2、Dependency injection in JavaScript
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
動(dòng)手寫一個(gè)angular版本的Message組件的方法
本篇文章主要介紹了動(dòng)手寫一個(gè)angular版本的Message組件的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12angular 實(shí)現(xiàn)同步驗(yàn)證器跨字段驗(yàn)證的方法
幾乎每個(gè)web應(yīng)用都會(huì)用到表單,那么驗(yàn)證器就是必不可少的東西,這篇文章主要介紹了angular 實(shí)現(xiàn)同步驗(yàn)證器跨字段驗(yàn)證的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04AngularJs Understanding Angular Templates
本文主要介紹AngularJs Understanding Angular Templates的資料,這里整理了詳細(xì)的資料及簡(jiǎn)單示例代碼,有興趣的小伙伴的參考下2016-09-09Angular網(wǎng)絡(luò)請(qǐng)求的封裝方法
本篇文章主要介紹了Angular網(wǎng)絡(luò)請(qǐng)求的封裝方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05詳解用webpack2搭建angular2的項(xiàng)目
本篇文章主要介紹了詳解用webpack2搭建angular2的項(xiàng)目 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06對(duì)angularJs中2種自定義服務(wù)的實(shí)例講解
今天小編就為大家分享一篇對(duì)angularJs中2種自定義服務(wù)的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09