自学实现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 相关文章推荐
Javascript中的变量使用说明
May 18 Javascript
css+js实现部分区域高亮可编辑遮罩层
Mar 04 Javascript
获取中文字符串的实际长度代码
Jun 05 Javascript
JavaScript匿名函数与委托使用示例
Jul 22 Javascript
javascript中Math.random()使用详解
Apr 15 Javascript
IE7浏览器窗口大小改变事件执行多次bug及IE6/IE7/IE8下resize问题
Aug 21 Javascript
jquery实现图片放大镜功能
Nov 23 Javascript
浅析Javascript的自动分号插入(ASI)机制
Sep 29 Javascript
JS实现匀加速与匀减速运动的方法示例
Sep 04 Javascript
javascript数组元素删除方法delete和splice解析
Dec 09 Javascript
vue实现学生信息管理系统
May 30 Javascript
vue3使用vuedraggable实现拖拽功能
Apr 06 Vue.js
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中实现记住密码自动登录的代码
2011/03/02 PHP
PHP抓屏函数实现屏幕快照代码分享
2014/01/02 PHP
递归实现php数组转xml的代码分享
2015/05/14 PHP
PHP实现防盗链的方法分析
2017/07/25 PHP
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
2013/01/24 Javascript
javascript实现tabs选项卡切换效果(自写原生js)
2013/03/19 Javascript
客户端js性能优化小技巧整理
2013/11/05 Javascript
关于onchange事件在IE和FF下的表现及解决方法
2014/03/08 Javascript
JavaScript验证图片类型(扩展名)的函数分享
2014/05/05 Javascript
jQuery常用操作方法及常用函数总结
2014/06/19 Javascript
基于JavaScript实现类似于百度学术高级检索功能
2016/03/02 Javascript
深入理解js函数的作用域与this指向
2016/05/28 Javascript
form表单转Json提交的方法(推荐)
2016/09/23 Javascript
JS实现中文汉字按拼音排序的方法
2017/10/09 Javascript
vue文件树组件使用详解
2018/03/29 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
详解如何在nuxt中添加proxyTable代理
2018/08/10 Javascript
解决vuecli3.0热更新失效的问题
2018/09/19 Javascript
微信小程序实现判断是分享到群还是个人功能示例
2019/05/03 Javascript
JavaScript实现五子棋游戏的方法详解
2019/07/08 Javascript
uin-app+mockjs实现本地数据模拟
2020/08/26 Javascript
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
[43:14]Liquid vs Optic 2018国际邀请赛淘汰赛BO3 第二场 8.21
2018/08/22 DOTA
Python网络编程中urllib2模块的用法总结
2016/07/12 Python
Python连接DB2数据库
2016/08/27 Python
Python使用getpass库读取密码的示例
2017/10/10 Python
Python中文编码知识点
2019/02/18 Python
python3使用matplotlib绘制条形图
2020/03/25 Python
使用Python给头像戴上圣诞帽的图像操作过程解析
2019/09/20 Python
python实现堆排序的实例讲解
2020/02/21 Python
TensorFlow中如何确定张量的形状实例
2020/06/23 Python
环保建议书300字
2014/05/14 职场文书
个人对照检查材料思想汇报
2014/09/26 职场文书
召开会议通知范文
2015/04/15 职场文书
毕业生学校组织意见
2015/06/04 职场文书
Mysql中mvcc各场景理解应用
2022/08/05 MySQL