AngularJS之依赖注入模拟实现


Posted in Javascript onAugust 19, 2016

一、概述 

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery 初体验(建议学习jquery)
Apr 25 Javascript
jquery tab插件精简版分享
Sep 10 Javascript
javascript中兼容主流浏览器的动态生成iframe方法
May 05 Javascript
基于JS实现简单的样式切换效果代码
Sep 04 Javascript
jQuery内容折叠效果插件用法实例分析(附demo源码)
Apr 28 Javascript
Node.js DES加密的简单实现
Jul 07 Javascript
JS对象是否拥有某属性如何判断
Feb 03 Javascript
微信小程序录音与播放录音功能
Dec 25 Javascript
详解Angular5路由传值方式及其相关问题
Apr 28 Javascript
javascript实现考勤日历功能
Nov 29 Javascript
使用p5.js实现动态GIF图片临摹重现
Oct 23 Javascript
在vue中通过render函数给子组件设置ref操作
Nov 17 Vue.js
AngularJS入门教程之XHR和依赖注入详解
Aug 18 #Javascript
JavaScript中函数声明与函数表达式的区别详解
Aug 18 #Javascript
Javascript中apply、call、bind的巧妙使用
Aug 18 #Javascript
AngularJS入门教程之双向绑定详解
Aug 18 #Javascript
AngularJS入门教程之迭代器过滤详解
Aug 18 #Javascript
AngularJS入门教程之AngularJS 模板
Aug 18 #Javascript
AngularJS入门教程之静态模板详解
Aug 18 #Javascript
You might like
PHP学习笔记之二
2011/01/17 PHP
php时间戳格式化显示友好的时间函数分享
2014/10/21 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
php实现ip白名单黑名单功能
2015/03/12 PHP
thinkPHP简单遍历数组方法分析
2016/05/16 PHP
thinkphp关于简单的权限判定方法
2017/04/03 PHP
PHP使用PhpSpreadsheet操作Excel实例详解
2020/03/26 PHP
制作高质量的JQuery Plugin 插件的方法
2010/04/20 Javascript
JavaScript接口实现代码 (Interfaces In JavaScript)
2010/06/11 Javascript
JavaScript isArray()函数判断对象类型的种种方法
2010/10/11 Javascript
JS循环遍历JSON数据的方法
2014/07/08 Javascript
使用AngularJS创建自定义的过滤器的方法
2015/06/18 Javascript
jquery仅用6行代码实现滑动门效果
2015/09/07 Javascript
jQuery同步提交示例代码
2015/12/12 Javascript
javascript 用函数实现继承详解
2016/05/28 Javascript
使用BootStrap实现用户登录界面UI
2016/08/10 Javascript
vue.js实现表格合并示例代码
2016/11/30 Javascript
详解Javascript获取缓存和清除缓存API
2017/05/25 Javascript
浅谈Vue Element中Select下拉框选取值的问题
2018/03/01 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
Python实现的用户登录系统功能示例
2018/02/05 Python
对python中的logger模块全面讲解
2018/04/28 Python
把csv文件转化为数组及数组的切片方法
2018/07/04 Python
python实现的config文件读写功能示例
2019/09/24 Python
Python使用pymysql模块操作mysql增删改查实例分析
2019/12/19 Python
python由已知数组快速生成新数组的方法
2020/04/08 Python
某公司.Net方向面试题
2014/04/24 面试题
计算机求职信
2013/12/01 职场文书
高中生家长会演讲稿
2014/01/14 职场文书
汽车装潢店创业计划书范文
2014/02/05 职场文书
法律专业求职信
2014/05/24 职场文书
党支部遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
运动会广播稿200字(10篇)
2014/10/12 职场文书
学院党委班子四风问题自查报告及整改措施
2014/10/25 职场文书
2015年会计个人工作总结
2015/04/02 职场文书
python数字图像处理数据类型及颜色空间转换
2022/06/28 Python