学习AngularJS的原因之一就是觉得他的DI很牛叉,为了更好的学习,在研究源码之前,想自己按照自己的思路先实现个DI,希望这个思路能够对
学习源码有帮助。
1 (function(){ 2 var config; 3 var di={}; 4 //用来缓存已经生成的对象 5 var beans=new Array(); 6 di.config=function(conf){ 7 this.config=conf; 8 return di; 9 }; 10 11 /*获取一个方法的参数列表,要求所有注入的参数都已$开头 12 第二个正则表达是后面的 g 很重要,它表示搜索到第一个之后接着往后搜 13 没有这个g ,就只能match第一个参数。 14 这里的参数列表指的就是代码中写的参数列表,不是带入参数后的参数类表 15 比如有一个方法: 16 function Fn(a,b,c,c1){} 17 参数列表就是一个有四个字符串组成的数组:["a","b","c","c1"] 18 因为需要有参数的名字去匹配参数的类型 19 */ 20 var getArguments=function(fun){ 21 return fun.toString().match(/(.*)/)[0].match(/$w+/g); 22 }; 23 //核心方法 24 di.getBean=function(beanName){ 25 //查看缓存,有的话直接返回 26 if(beans[beanName]!=undefined) 27 return beans[beanName]; 2929 var fn=this.config[beanName]; 30 if(fn==undefined) return; 31 var args=getArguments(fn); 32 var argss=new Array(); 33 var objstr=""; 34 var index=0; 35 var obj; 36 for(var i in args){ 37 argss[i]=(di.getBean(args[i])); 38 objstr+="argss["+index+"],"; 39 index++; 40 } 41 objstr=objstr.substring(0, objstr.length-1); 42 objstr="obj=new (fn)("+objstr+"); "; 43 /*整个DI能够实现就靠这个eval方法了, 44 它接受一个String参数,并把String里面的内容按照Javasript的标准编译并执行 45 最牛叉的是eval里面的变量也遵循Javascript的函数作用域:也就是变量可以定义在eval外面 46 */ 47 /* 48 当然这里也可以用apply,或者call,不过总有这样那样的问题,可能是自己对这两个方法不够了解吧 49 先用eval实现了,以后再研究apply 和call 50 */ 51 eval(objstr); 52 beans[beanName]=obj; 53 return obj; 54 }; 55 window.di=di; 56 })(window);
下面是个例子,
1 function Person(){ 2 this.name="Mike"; 3 this.address="China"; 4 5 this.getName=function(){ 6 return this.name; 7 }; 8 this.getAddress=function(){ 9 return this .address; 10 }; 11 } 12 function Service($person){ 13 this.work=function(){ 14 return $person.getName()+" is living in "+$person.getAddress(); 15 }; 16 } 17 18 function Adaptor($person,$service){ 19 this.alertt=function(){ 20 var k=$service.work(); 21 alert(k); 22 }; 23 }
有Person,Service,Adaptor三个类,Service类依赖Person来组装语句,Adaptor类依赖Service类来显示,只要有下面的代码就能运行了
var conf={ "$person":Person, "$adaptor":Adaptor, "$service":Service }; di.config(conf).getBean("$adaptor").alertt();
目前这种DI有个缺陷就是没法再组装时决定类的状态。比如一开始new一个Person时想通过构造方法给name赋值,以上的方式是不能直接做不到的。
只能通过一个间接地方式,如下:
function Person(){ this.name="Mike"; this.getName=function(){ return this.name; }; this.setName=function($name){ this.name=$name.Name; }; } function Name(){ this.NAME="Tom"; } //然后在Service中使用Person前重新给name赋值 function Service($person,$name){ this.work=function(){ $person.setName($name.NAME); return $person.getName()+" is living in China"; //这里就成了 Tom is living in China }; }