自学实现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 相关文章推荐
List the Codec Files on a Computer
Jun 11 Javascript
JavaScript 字符串与数组转换函数[不用split与join]
Dec 13 Javascript
跟着Jquery API学Jquery之一 选择器
Apr 07 Javascript
JS日期和时间选择控件升级版(自写)
Aug 02 Javascript
Textarea根据内容自适应高度
Oct 28 Javascript
jquery跨域请求示例分享(jquery发送ajax请求)
Mar 25 Javascript
Js动态设置rem来实现移动端字体的自适应代码
Oct 14 Javascript
jQuery特殊符号转义的实现
Nov 30 Javascript
JavaScript程序设计高级算法之动态规划实例分析
Nov 24 Javascript
图片懒加载imgLazyLoading.js使用详解
Sep 15 Javascript
vue组件name的作用小结
May 23 Javascript
Ant Design moment对象和字符串之间的相互转化教程
Oct 27 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基于curl扩展制作跨平台的restfule 接口
2015/05/11 PHP
非常好的js代码
2006/06/27 Javascript
Google Suggest ;-) 基于js的动态下拉菜单
2006/10/11 Javascript
js数字输入框(包括最大值最小值限制和四舍五入)
2009/11/24 Javascript
基于jquery的DIV随滚动条滚动而滚动的代码
2012/07/20 Javascript
javascript获取ckeditor编辑器的值(实现代码)
2013/11/18 Javascript
常规表格多表头查询示例
2014/02/21 Javascript
Bootstrap Navbar Component实现响应式导航
2016/10/08 Javascript
JavaScript和jQuery获取input框的绝对位置实现方法
2016/10/13 Javascript
防止重复发送 Ajax 请求
2017/02/15 Javascript
jquery.masonry瀑布流效果
2017/05/25 jQuery
网页中的图片查看器viewjs使用方法
2017/07/11 Javascript
基于BootStrap multiselect.js实现的下拉框联动效果
2017/07/28 Javascript
JS实现元素上下左右移动效果
2017/10/18 Javascript
JavaScript判断日期时间差的实例代码
2018/03/01 Javascript
详解微信小程序canvas圆角矩形的绘制的方法
2018/08/22 Javascript
详解Vue 动态组件与全局事件绑定总结
2018/11/11 Javascript
关于layui导航栏不展示下拉列表的解决方法
2019/09/25 Javascript
vue项目中在可编辑div光标位置插入内容的实现代码
2020/01/07 Javascript
vue 解决addRoutes多次添加路由重复的操作
2020/08/04 Javascript
python读取word文档的方法
2015/05/09 Python
Python判断中文字符串是否相等的实例
2018/07/06 Python
tensorflow使用神经网络实现mnist分类
2018/09/08 Python
Python随机生成身份证号码及校验功能
2018/12/04 Python
python requests post多层字典的方法
2018/12/27 Python
使用OpCode绕过Python沙箱的方法详解
2019/09/03 Python
pandas-resample按时间聚合实例
2019/12/27 Python
python 删除excel表格重复行,数据预处理操作
2020/07/06 Python
世界各地的当地人的食物体验:Eatwith
2019/07/26 全球购物
数百万免费的图形资源:Freepik
2020/09/21 全球购物
中间件的定义
2016/08/09 面试题
服装设计行业个人的自我评价
2013/12/20 职场文书
茶叶生产计划书
2014/01/10 职场文书
公司活动策划方案
2014/01/13 职场文书
服务承诺书范文
2014/05/19 职场文书
2015年幼儿园个人工作总结
2015/04/25 职场文书