自学实现angularjs依赖注入


Posted in Javascript onDecember 20, 2016

    在用angular依赖注入时,感觉很好用,他的出现是 为了“削减计算机程序的耦合问题” ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个这么牛逼的功能。于是就模仿着写了一个,如果有什么不对,请大家批评指正。

     其实刚开始的时候我也不知道怎么下手,源码中有些确实晦涩难懂,到现在我也没有看明白,于是我就静下心想一想,他是怎么用的,如下所示:

angular.module(/*省略*/)
 .factory("xxxService", ['xx',function (xx) {
  return {
    //省略
  }
 }])
 .controller('iiController',['xxxService',function(xxxService){
  //省略
 }]);
/*...方法省略..*/

    看看上面严格模式下的使用方式,先不去看源码,如何实现service重用,controller不重用呢? 我就按照自己的想法创建一个cache用于保存service,controller 只运行一次,不保存到cache中。

有了点思路,就把该有的东西先写了,

(function (global) {
 function CreateInjector(cache){
  this.cache=cache;//用于保存service的cache
 }
 function Angular(){}
 Angular.module=function(){
  var moduleObj={};
  return {
   injector:new CreateInjector(moduleObj),
   factory:function(name,fn){
   },
   controller:function(name,fn){
   }
  }
 };
 global.angular = Angular;
})(window);

    上面两个构造函数,一个是创建构造器,一个是angular 的静态module(不用创建直接用 "构造函数名.方法名",这里就是想模仿angular.module())方法,这里angular module 的方法我简写了,他也有依赖注入,但是我能力有限,先研究了controller和service的注入。上面的方法名字都是我copy于源码中的,这里我就不解释他们的具体意义了。

由于我们研究的是依赖注入,真正的核心代码如下:

var ARROW_ARG = /^([^\(]+?)=>/;
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function isArray(obj){
 return Object.prototype.toString.call(obj) === '[object Array]';
}
function stringifyFn(fn) {
 return fn.toString();
}

function extractArgs(fn) {
 var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
 args= fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
 return args[1].split(',');
}
function CreateInjector(cache){
 this.cache=cache;
}
CreateInjector.prototype={
 constructor:CreateInjector,
 invoke:function(fn,self){
  var argsName= extractArgs(fn),argsFn=[];
  argsName.forEach(function(arg){
   argsFn.push(this.cache[arg]);
  },this);
  if(isArray(fn)){
   return fn[fn.length-1].apply(self,argsFn);
  }else{
   return fn.apply(self,argsFn);
  }
 }
};

其中上面的正则表达式是复制于源码中的,代码原理是:

   (1)把传来的function toString(),然后用正则match出所传参数名,用split把参数分割成参数数组;

   (2)循环参数数组,在cache中找到此参数名下的函数,push到一个函数数组中;

   (3)利用apply,把函数数组当成参数,去执行函数;

对于所传的fn, 判断是数组格式,还是普通的,如果是数组就apply最后的一个值,也就是函数,否则就是fn自己。

   上面的我没有做错误处理,比如注入的找不到等等一些问题,有兴趣自己加上吧。

最后所有代码如下,大家可以复制运行:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>angular injector Demo</title>
</head>
<body>
<script>
(function (global) {
  var ARROW_ARG = /^([^\(]+?)=>/;
  var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
  var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
  function isArray(obj){
    return Object.prototype.toString.call(obj) === '[object Array]';
  }
  function stringifyFn(fn) {
    return fn.toString();
  }

  function extractArgs(fn) {
    var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
    args= fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
    return args[1].split(',');
  }
  function CreateInjector(cache){
    this.cache=cache;
  }
  CreateInjector.prototype={
    constructor:CreateInjector,
    invoke:function(fn,self){
      var argsName= extractArgs(fn),argsFn=[];
      argsName.forEach(function(arg){
        argsFn.push(this.cache[arg]);
      },this);
      if(isArray(fn)){
        return fn[fn.length-1].apply(self,argsFn);
      }else{
        return fn.apply(self,argsFn);
      }
    }
  };
  function Angular(){}
  Angular.module=function(){
    var moduleObj={};
     return {
       injector:new CreateInjector(moduleObj),
       factory:function(name,fn){
         moduleObj[name]=this.injector.invoke(fn);
         return this;
       },
       controller:function(name,fn){
         this.injector.invoke(fn);
         return this;
       }
     }
  };
  global.angular = Angular;
})(window);


angular.module()
  .factory('cacheService',[function(){
    return {};
  }])
  .factory("userInfoService", ['cacheService',function (cacheService) {
    return {
      getUserInfo:function(){
        return cacheService.userInfo;
      },
      setUserInfo:function(value){
        cacheService.userInfo=value;
      }
    }
  }])
  .controller('userController',['userInfoService',function(userInfoService){
    userInfoService.setUserInfo({id:'qqqq11234',name:'zhangLearing'});
    console.log(userInfoService.getUserInfo());
  }]);
</script>
</body>
</html>

谢谢大家!

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

Javascript 相关文章推荐
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
Mar 16 Javascript
javascript写的日历类(基于pj)
Dec 28 Javascript
js/jquery获取浏览器窗口可视区域高度和宽度以及滚动条高度实现代码
Dec 17 Javascript
jquery.post用法示例代码
Jan 03 Javascript
BootStrap selectpicker
Jun 20 Javascript
Vue 短信验证码组件开发详解
Feb 14 Javascript
微信小程序手势操作之单触摸点与多触摸点
Mar 10 Javascript
BootStrap Table前台和后台分页对JSON格式的要求
Jun 28 Javascript
Node.js pipe实现源码解析
Aug 12 Javascript
weex slider实现滑动底部导航功能
Aug 28 Javascript
vue resource post请求时遇到的坑
Oct 19 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
Sep 15 Javascript
JS多物体实现缓冲运动效果示例
Dec 20 #Javascript
详解js中Number()、parseInt()和parseFloat()的区别
Dec 20 #Javascript
JavaScript 限制文本框不可输入英文单双引号的方法
Dec 20 #Javascript
用jQuery.ajaxSetup实现对请求和响应数据的过滤
Dec 20 #Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
Dec 20 #Javascript
js多个物体运动功能实例分析
Dec 20 #Javascript
JS高级运动实例分析
Dec 20 #Javascript
You might like
PHP小程序自动提交到自助友情连接
2009/11/24 PHP
PHP命名空间(namespace)的动态访问及使用技巧
2014/08/18 PHP
php获取文章上一页与下一页的方法
2014/12/01 PHP
php自动更新版权信息显示的方法
2015/06/19 PHP
PHP基于yii框架实现生成ICO图标
2015/11/13 PHP
实例简介PHP的一些高级面向对象编程的特性
2015/11/27 PHP
SAE实时日志接口SDK用法示例
2016/10/09 PHP
thinkPHP+ajax实现统计页面pv浏览量的方法
2017/03/15 PHP
获取客户端网卡MAC地址和IP地址实现JS代码
2013/03/17 Javascript
javascript获取鼠标位置部分的实例代码(兼容IE,FF)
2013/08/05 Javascript
java与javascript之间json格式数据互转介绍
2013/10/29 Javascript
实现音乐播放器的代码(html5+css3+jquery)
2015/08/04 Javascript
详解Angular中的自定义服务Service、Provider以及Factory
2017/04/22 Javascript
详解Vue2中组件间通信的解决全方案
2017/07/28 Javascript
js注册时输入合法性验证方法
2017/10/21 Javascript
karma+webpack搭建vue单元测试环境的方法示例
2018/05/24 Javascript
mpvue将vue项目转换为小程序
2018/09/30 Javascript
谈谈我在vue-cli3中用预渲染遇到的坑
2020/04/22 Javascript
Python实现从url中提取域名的几种方法
2014/09/26 Python
python使用PythonMagick将jpg图片转换成ico图片的方法
2015/03/26 Python
Python中几种操作字符串的方法的介绍
2015/04/09 Python
Python入门之modf()方法的使用
2015/05/15 Python
Python中__init__.py文件的作用详解
2016/09/18 Python
selenium+python实现1688网站验证码图片的截取功能
2018/08/14 Python
python绘制地震散点图
2019/06/18 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
2020/03/24 Python
关于keras.layers.Conv1D的kernel_size参数使用介绍
2020/05/22 Python
Python单元测试及unittest框架用法实例解析
2020/07/09 Python
纯CSS3实现表单验证效果(非常不错)
2017/01/18 HTML / CSS
复古风格的女装和装饰品:ModCloth
2017/12/29 全球购物
阿姆斯特丹城市卡:Amsterdam Pass
2019/12/01 全球购物
OnePlus加拿大官网:中国国际化手机品牌
2020/10/13 全球购物
省文明单位申报材料
2014/05/08 职场文书
理财计划书
2014/08/14 职场文书
2014年宣传部工作总结
2014/11/12 职场文书
丧事答谢词
2015/01/05 职场文书