zoukankan      html  css  js  c++  java
  • angularjs 依赖注入原理与实现

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

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

     1 angular.module(/*省略*/)
     2    .factory("xxxService", ['xx',function (xx) {
     3         return {
     4              //省略
     5         }
     6    }])
     7    .controller('iiController',['xxxService',function(xxxService){
     8        //省略
     9    }]);
    10 /*...方法省略..*/

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

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

     1 (function (global) {
     2     function CreateInjector(cache){
     3         this.cache=cache;//用于保存service的cache
     4     }
     5     function Angular(){}
     6     Angular.module=function(){
     7         var moduleObj={};
     8         return {
     9             injector:new CreateInjector(moduleObj),
    10             factory:function(name,fn){
    11             },
    12             controller:function(name,fn){
    13             }
    14         }
    15     };
    16     global.angular = Angular;
    17 })(window);

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

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

     1 var ARROW_ARG = /^([^(]+?)=>/;
     2 var FN_ARGS = /^[^(]*(s*([^)]*))/m;
     3 var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
     4 function isArray(obj){
     5     return Object.prototype.toString.call(obj) === '[object Array]';
     6 }
     7 function stringifyFn(fn) {
     8     return fn.toString();
     9 }
    10 
    11 function extractArgs(fn) {
    12     var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
    13     args= fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
    14     return args[1].split(',');
    15 }
    16 function CreateInjector(cache){
    17     this.cache=cache;
    18 }
    19 CreateInjector.prototype={
    20     constructor:CreateInjector,
    21     invoke:function(fn,self){
    22         var argsName= extractArgs(fn),argsFn=[];
    23         argsName.forEach(function(arg){
    24             argsFn.push(this.cache[arg]);
    25         },this);
    26         if(isArray(fn)){
    27             return fn[fn.length-1].apply(self,argsFn);
    28         }else{
    29             return fn.apply(self,argsFn);
    30         }
    31     }
    32 };

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

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

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

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

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

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

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

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>angular injector Demo</title>
     6 </head>
     7 <body>
     8 <script>
     9 (function (global) {
    10     var ARROW_ARG = /^([^(]+?)=>/;
    11     var FN_ARGS = /^[^(]*(s*([^)]*))/m;
    12     var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
    13     function isArray(obj){
    14         return Object.prototype.toString.call(obj) === '[object Array]';
    15     }
    16     function stringifyFn(fn) {
    17         return fn.toString();
    18     }
    19 
    20     function extractArgs(fn) {
    21         var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
    22         args= fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
    23         return args[1].split(',');
    24     }
    25     function CreateInjector(cache){
    26         this.cache=cache;
    27     }
    28     CreateInjector.prototype={
    29         constructor:CreateInjector,
    30         invoke:function(fn,self){
    31             var argsName= extractArgs(fn),argsFn=[];
    32             argsName.forEach(function(arg){
    33                 argsFn.push(this.cache[arg]);
    34             },this);
    35             if(isArray(fn)){
    36                 return fn[fn.length-1].apply(self,argsFn);
    37             }else{
    38                 return fn.apply(self,argsFn);
    39             }
    40         }
    41     };
    42     function Angular(){}
    43     Angular.module=function(){
    44         var moduleObj={};
    45          return {
    46              injector:new CreateInjector(moduleObj),
    47              factory:function(name,fn){
    48                  moduleObj[name]=this.injector.invoke(fn);
    49                  return this;
    50              },
    51              controller:function(name,fn){
    52                  this.injector.invoke(fn);
    53                  return this;
    54              }
    55          }
    56     };
    57     global.angular = Angular;
    58 })(window);
    59 
    60 
    61 angular.module()
    62     .factory('cacheService',[function(){
    63         return {};
    64     }])
    65     .factory("userInfoService", ['cacheService',function (cacheService) {
    66         return {
    67             getUserInfo:function(){
    68                 return cacheService.userInfo;
    69             },
    70             setUserInfo:function(value){
    71                 cacheService.userInfo=value;
    72             }
    73         }
    74     }])
    75     .controller('userController',['userInfoService',function(userInfoService){
    76         userInfoService.setUserInfo({id:'qqqq11234',name:'zhangLearing'});
    77         console.log(userInfoService.getUserInfo());
    78     }]);
    79 </script>
    80 </body>
    81 </html>

       谢谢大家!

  • 相关阅读:
    基于libevent的TLS单向认证CS通信验证
    ubuntu按照时间顺序列出apt安装的程序
    网页识别语音插件annyang可以实现识别中文
    微信小程序图片和签名
    linux run/media/wang/centos_磁盘爆满
    一个页面实现增删改查
    查某关键字在数据库中的哪个位置
    ADO.NET五大对象
    怎样获取当前时间
    string与stringbuilder的区别
  • 原文地址:https://www.cnblogs.com/zhangkunweb/p/6201768.html
Copyright © 2011-2022 走看看