自学实现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 相关文章推荐
js资料prototype 属性
Mar 13 Javascript
网页自动刷新,不产生嗒嗒声的一个解决方法
Mar 27 Javascript
脚本安需导入(装载)的三种模式的对比
Jun 24 Javascript
实现超用户体验 table排序javascript实现代码
Jun 22 Javascript
js获取html文件的思路及示例
Sep 17 Javascript
弹出最简单的模式化遮罩层的js代码
Dec 04 Javascript
jQuery选择器全面总结
Jan 06 Javascript
jQuery中Dom的基本操作小结
Jan 23 Javascript
JavaScript原生xmlHttp与jquery的ajax方法json数据格式实例
Dec 04 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
Dec 17 Javascript
详解如何为你的angular app构建一个第三方库
Dec 07 Javascript
JS中一些高效的魔法运算符总结
May 06 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性能分析工具XHProf安装使用教程
2015/05/13 PHP
php arsort 数组降序排序详细介绍
2016/11/17 PHP
PHP面向对象之工作单元(实例讲解)
2017/06/26 PHP
PHP数组遍历的几种常见方式总结
2019/02/15 PHP
使用Git实现Laravel项目的自动化部署
2019/11/24 PHP
原型方法的不同写法居然会影响调试的解决方法
2007/03/08 Javascript
教你如何解密js/vbs/vbscript加密的编码异处理小结
2008/06/25 Javascript
TextArea 控件的最大长度问题(js json)
2009/12/16 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
js 获取本地文件及目录的方法(推荐)
2016/11/10 Javascript
nodejs接入阿里大鱼短信验证码的方法
2017/07/10 NodeJs
浅谈webpack对样式的处理
2018/01/05 Javascript
微信小程序仿微信运动步数排行(交互)
2018/07/13 Javascript
小程序文字跑马灯效果
2018/12/28 Javascript
使用jquery-easyui的布局layout写后台管理页面的代码详解
2019/06/19 jQuery
js实现倒计时秒杀效果
2020/03/25 Javascript
Vue.js的模板语法详解
2020/02/16 Javascript
JavaScript如何实现防止重复的网络请求的示例
2021/01/28 Javascript
vue监听键盘事件的相关总结
2021/01/29 Vue.js
Python获取apk文件URL地址实例
2013/11/01 Python
python字典排序实例详解
2015/05/20 Python
Python程序中的观察者模式结构编写示例
2016/05/27 Python
用Python实现命令行闹钟脚本实例
2016/09/05 Python
Python生成随机密码的方法
2017/06/16 Python
Python cookbook(数据结构与算法)对切片命名清除索引的方法
2018/03/13 Python
python urllib爬虫模块使用解析
2019/09/05 Python
python修改FTP服务器上的文件名
2019/09/11 Python
Python打包模块wheel的使用方法与将python包发布到PyPI的方法详解
2020/02/12 Python
Speedo速比涛德国官方网站:世界领先的泳装品牌
2019/08/26 全球购物
如果让你测试一台高速激光打印机,你都会进行哪些测试
2012/12/04 面试题
八一建军节活动方案
2014/02/10 职场文书
暑假安全教育广播稿
2014/09/10 职场文书
2016年少先队活动总结
2016/04/06 职场文书
读《工匠精神》有感:热爱工作,精益求精
2019/12/28 职场文书
python实现简单的井字棋
2021/05/26 Python
python运行脚本文件的三种方法实例
2022/06/25 Python