Angular依賴注入詳解

Angular算是將後端開發工程化引入前端的先驅之一,而Dependency injection依賴注入(後面簡稱爲DI)又是Angular內部運做的核心功能,因此要深刻理解Angular有必要先理解這一核心概念。前端

維基百科對依賴注入的解釋

在軟件工程中,依賴注入是實現控制反轉的一種軟件設計模式,一個依賴是一個被其餘對象(client)調用的對象(服務),注入則是將被依賴的對象(service)實例傳遞給依賴對象(client)的行爲。將 被依賴的對象傳給依賴者,而不須要依賴者本身去建立或查找所需對象是DI的基本原則。 依賴注入容許程序設計聽從依賴倒置原則(簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就下降了客戶與實現模塊間的耦合) 調用者(client)只需知道服務的接口,具體服務的查找和建立由注入者(injector)負責處理並提供給client,這樣就分離了服務和調用者的依賴,符合低耦合的程序設計原則。git

依賴注入中的角色

從維基百科解釋可知, DI中包含三個角色,調用者(client), 服務(service)和注入者 (injector),下面開始介紹本文的主題 Angular的依賴注入。github

Angular依賴注入分析

先看看下面這段 hello,world代碼 (注意:設置了嚴格模式或壓縮混淆代碼後 下面的代碼不能正常工做,後面有解釋)編程

angular.module('myApp', [])
  .controller('Ctl', function ($scope, $log) {
    $scope.name = 'leonwgc';
    $log.log('hello,world');
  });

 

上面這段代碼就用到了angular的依賴注入,代碼首先建立了一個myApp模塊,而後在此模塊中建立了Ctl控制器,建立控制器函數的第二個參數則是控制器的構造函數, 構造函數聲明瞭對$scope和$log服務的依賴。 當構造函數執行時, 便可得到$scope和$log服務實例,進行操做。 從咱們前面對DI的瞭解,$scope和$log是由注入器injector 提供,知道了injector的存在,咱們直接從angular的源碼中將其找出,以下:後端

function createInternalInjector(cache, factory) {
    // 中間一段略去...

    // 調用client
    function invoke(fn, self, locals, serviceName) {
      if (typeof locals === 'string') {
        serviceName = locals;
        locals = null;
      }

      var args = [],
          // 查詢依賴
          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
          length, i,
          key;

      // 中間一段略去...
      // 遍歷$inject數組調用getService獲取服務....

      //開始執行client , args則是依賴的所有服務,injector都爲咱們建立好了
      return fn.apply(self, args);
    }

    // 中間一段略去...

    // 這裏返回公開的injector對象 
    return {
      // 執行DI方法,好比上面的控制器函數
      // invoke方法首先就是調用annotate取得依賴
      // 而後調用get取得服務
      // 若是緩存中沒有服務,get內部調用instantiate建立服務並緩存
      // 最後利用function.apply傳入依賴並執行
      invoke: invoke,
      // 實例化(建立)服務
      instantiate: instantiate,
      // 獲取服務(若是緩存中有,直接從緩存拿,沒有則調用instantiate建立並放入緩存,下次直接從緩存拿)
      get: getService,
      // 得到依賴服務
      annotate: createInjector.$$annotate,
      // 檢查緩存中是否包含服務
      has: function(name) {
        return providerCache.hasOwnProperty(name + providerSuffix)
         || cache.hasOwnProperty(name);
      }
    };
  }

 

源碼中查詢依賴的源碼以下:設計模式

function annotate(fn, strictDi, name) {
  var $inject,
      fnText,
      argDecl,
      last;

  if (typeof fn === 'function') {
    // 若是咱們直接給函數添加了$inject依賴
    // 則直接返回依賴,後面不作處理
    if (!($inject = fn.$inject)) {
      $inject = [];
      if (fn.length) {
        if (strictDi) {
          if (!isString(name) || !name) {
            name = fn.name || anonFn(fn);
          }
          throw $injectorMinErr('strictdi',
'{0} is not using explicit annotation...', name);
        }
        // 針對直接在構造函數中使用服務的狀況
        // 使用function.toString() 而後正則匹配出依賴的對象
        // 因此上面例子若是混淆了代碼就呵呵了
        // 最後存入$inject數組
        fnText = fn.toString().replace(STRIP_COMMENTS, '');
        argDecl = fnText.match(FN_ARGS);
        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
          arg.replace(FN_ARG, function(all, underscore, name) {
            $inject.push(name);
          });
        });
      }
      //給構造函數添加$inject屬性
      fn.$inject = $inject;
    }
  } else if (isArray(fn)) {
    last = fn.length - 1;
    assertArgFn(fn[last], 'fn');
    // 若是是數組格式,則依賴對象是數組的第一個到倒數第二個對象
    // 要調用的函數則是數組的最後一個元素
    $inject = fn.slice(0, last);
  } else {
    assertArgFn(fn, 'fn', true);
  }
  // 返回依賴數組
  return $inject;
}

 

看了上面的源碼片斷和解釋,想必你們對angular的依賴注入有了總體的認識。數組

下面是另外兩種推薦的聲明依賴的方式緩存

1. 數組註釋 (推薦), js壓縮混淆不會有影響。

angular.module('myApp', [])
  .controller('Ctl', ['$scope', '$log', function ($scope, $log) {
  $scope.name = 'leonwgc';
  $log.log('hello,world');
}]);

 

2.$inject 屬性 ,js壓縮混淆不會有影響

angular.module('myApp', [])
  .controller('Ctl', Ctrl);

function Ctrl($scope, $log) {
  $scope.name = 'leonwgc';
  $log.log('hello,world');
}

// 給構造函數添加$inject屬性,
// $inject是一個數組,元素是依賴的服務名.
Ctrl.$inject = ["$scope", "$log"];

 

Angular引入了大量後端開發的概念,而前端同窗可能還不熟悉,望本文能有所幫助。app

相關文章
相關標籤/搜索