首页 > 编程 > JavaScript > 正文

AngularJS之依赖注入模拟实现

2019-11-20 09:12:37
字体:
来源:转载
供稿:网友

一、概述 

AngularJS有一经典之处就是依赖注入,对于什么是依赖注入,熟悉spring的同学应该都非常了解了,但,对于前端而言,还是比较新颖的。 

依赖注入,简而言之,就是解除硬编码,达到解偶的目的。 

下面,我们看看AngularJS中常用的实现方式。 

方法一:推断式注入声明,假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,然后通过$injector将这些参数注入进对象实例。 

如下: 

//方法一:推断式注入声明,假定参数名称就是依赖的名称。//因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,//然后通过$injector将这些参数注入进对象实例injector.invoke(function($http, $timeout){ //TODO});

方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。
 如下:

//方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,//数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。module.controller('name', ['$http', '$timeout', function($http, $timeout){ //TODO}]); 

看了上述代码,心中有一疑问,这些是怎么实现的呢? 

哈哈,下面,我们就来一起模拟一下这些依赖注入方式,从而了解它们。 

二、搭建基本骨架 

依赖注入的获取过程就是,通过字段映射,从而获取到相应的方法。 

故而,要实现一个基本的依赖注入,我们需要一个存储空间(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>

五、参考 

1、AngularJS应用开发思维之3:依赖注入 
2、Dependency injection in JavaScript

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表