1. 程式人生 > >angular依賴注入:angular 依賴注入原理

angular依賴注入:angular 依賴注入原理

依賴注入(Dependency Injection,簡稱DI)是像C#,java等典型的面嚮物件語言框架設計原則控制反轉的一種典型的一種實現方式,angular把它引入到js中,介紹angular依賴注入的使用方式的文章很多,
angular官方的文件,也有很詳細的說明。但介紹原理的較少,angular程式碼結構較複雜,文章實現了一簡化版本的DI,核心程式碼只有30行左右,相看實現效果(可能需FQ)或檢視原始碼

這篇文章用盡量簡單的方式說一說 angular依賴注入的實現。

簡化的實現原理

要實現注入,基本有三步:

  1. 得到模組的依賴項
  2. 查詢依賴項所對應的物件
  3. 執行時注入

1. 得到模組的依賴項

javascript 實現DI的核心api是Function.prototype.toString

,對一個函式執行toString,它會返回函式的原始碼字串,這樣我們就可以通過正則匹配的方式拿到這個函式的引數列表:

functionextractArgs(fn) { //angular 這裡還加了註釋、箭頭函式的處理
            var args = fn.toString().match(/^[^\(]*\(\s*([^\)]*)\)/m);
            return args[1].split(',');
        }

2. 查詢依賴項所對應的物件

java與.net通過反射來獲取依賴物件,js是動態語言,直接一個object[name]就可以直接拿到物件。所以只要用一個物件儲存物件或函式列表就可以了

functioncreateInjector(cache) {
            this.cache = cache;

        }
angular.module = function () {
            modules = {};
            injector = new createInjector(modules);
            return {
                injector: injector,
                factory: function (name, fn) {
                    modules[name
.trim()] = this.injector.invoke(fn); return this; } } };

3. 執行時注入

最後通過 fn.apply方法把執行上下文,和依賴列表傳入函式並執行:


createInjector.prototype = {
            invoke: function (fn, self) {
                argsString = extractArgs(fn);
                args = [];
                argsString.forEach(function (val) {
                    args.push(this.cache[val.trim()]);
                }, this);
                return fn.apply(self, args);
            }
        };

這裡是簡化的版本,實際angular的實現考慮了很多問題,如模組管理,延遲執行等

angular 的實現

為了簡單,我們也按這三步來介紹angular DI

  1. 得到模組的依賴項
  2. 查詢依賴項所對應的物件
  3. 執行時注入

注:以下程式碼行數有就可能變

1. 得到模組的依賴項

var ARROW_ARG = /^([^\(]+?)=>/;
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;

functionextractArgs(fn) {
  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
      args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
  return args;
}

2. 查詢依賴項所對應的物件

    functiongetService(serviceName, caller) {
      if (cache.hasOwnProperty(serviceName)) {
        if (cache[serviceName] === INSTANTIATING) {
          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
                    serviceName + ' <- ' + path.join(' <- '));
        }
        return cache[serviceName];
      } else {
        try {
          path.unshift(serviceName);
          cache[serviceName] = INSTANTIATING;
          return cache[serviceName] = factory(serviceName, caller);
        } catch (err) {
          if (cache[serviceName] === INSTANTIATING) {
            delete cache[serviceName];
          }
          throw err;
        } finally {
          path.shift();
        }
      }
    }

3. 執行時注入

得到引數:

    functioninjectionArgs(fn, locals, serviceName) {
      var args = [],
          $inject = createInjector.$$annotate(fn, strictDi, serviceName);

      for (var i = 0, length = $inject.length; i < length; i++) {
        var key = $inject[i];
        if (typeof key !== 'string') {
          throw $injectorMinErr('itkn',
                  'Incorrect injection token! Expected service name as string, got {0}', key);
        }
        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
                                                         getService(key, serviceName));
      }
      return args;
    }

呼叫

    functioninvoke(fn, self, locals, serviceName) {
      if (typeof locals === 'string') {
        serviceName = locals;
        locals = null;
      }

      var args = injectionArgs(fn, locals, serviceName);
      if (isArray(fn)) {
        fn = fn[fn.length - 1];
      }

      if (!isClass(fn)) {
        // http://jsperf.com/angularjs-invoke-apply-vs-switch
        // #5388
        return fn.apply(self, args);
      } else {
        args.unshift(null);
        return new (Function.prototype.bind.apply(fn, args))();
      }
    }

angular模組管理,深坑

angular在每次應用啟動時,初始化一個Injector例項:

var injector = createInjector(modules, config.strictDi);

由此程式碼可以看出對每一個Angular應用來說,無論是哪個模組,所有的"provider"都是存在相同的providerCache或cache中

所以會導致一個被譽為angular模組管理的坑王的問題:
module 並沒有什麼名稱空間的作用,當依賴名相同的時候,後面引用的會覆蓋前面引用的模組。

具體的示例可以檢視:

注:angular di用本文的呼叫方式壓縮程式碼會出問題:可以用g-annotate轉為安全的呼叫方式。

到此angular di的實現原理已完成簡單的介紹,angular用了專案中幾乎不會用到的api:Function.prototype.toString 實現依賴注入,思路比較簡單,但實際框架中考慮的問題較多,更加詳細的實現可以直接看angular的原始碼

以後會逐步介紹angular其它原理。

相關推薦

angular依賴注入angular 依賴注入原理

依賴注入(Dependency Injection,簡稱DI)是像C#,java等典型的面嚮物件語言框架設計原則控制反轉的一種典型的一種實現方式,angular把它引入到js中,介紹angular依賴注入的使用方式的文章很多,angular官方的文件,也有很詳細的說明。但

30行程式碼讓你理解angular依賴注入angular 依賴注入原理

依賴注入(Dependency Injection,簡稱DI)是像C#,java等典型的面嚮物件語言框架設計原則控制反轉的一種典型的一種實現方式,angular把它引入到js中,介紹angular依賴注入的使用方式的文章很多, angular官方的文件,也有很詳細的說明。但介紹原理的較少,angular程式碼

Angular總結二Angular 啟動過程

environ test 元數據 引入 命令行工具 選擇 mage htm 內容 要弄清楚 Angular 的啟動過程,就要弄明白 Angular 啟動時加載了哪個頁面,加載了哪些腳本,這些腳本做了哪些事? 通過 Angular 的編譯依賴文件 .angular-cli.j

Spark2.3.2原始碼解析 5. RDD 依賴關係依賴與窄依賴

    Spark中RDD的高效與DAG(有向無環圖)有很大的關係,在DAG排程中需要對計算的過程劃分Stage,劃分的依據就是RDD之間的依賴關係。RDD之間的依賴關係分為兩種,寬依賴(wide dependency/shuffle dependency)和窄依賴(narrow

Maven依賴調解解決依賴衝突

Maven 依賴調解的第一原則:路徑最短者優先。 假設專案 A 中存在如下依賴關係: A -> B -> C -> X(V1) A -> D -> X(V2) X(V1)的路徑深度為 3,X(V2) 的路徑深度為 2,X(V2) 的路徑深度比

[Angular 2] 翻譯Angular 2 服務是不是單例?

這篇文章的目的是幫助大家理解 Angular 2 基於依賴注入的作用域如何工作,需要明白元件,服務等相關知識以及對依賴注入的基本瞭解。 – Angular 2 服務是不是單例? – 某種意義上來說,是的。。。 – 兄弟,你說的貌似沒什麼幫助啊。。。 – 那好吧

Angular依賴注入類“AnotherProductService”錯誤實現類“ProductService”。你是想擴充套件“ProductService”並將其成員作為子類繼承嗎?

在做Angular依賴注入例項時候出現一個問題“類“AnotherProductService”錯誤實現類“ProductService”。你是想擴充套件“ProductService”並將其成員作為子類繼承嗎?”於是找到解決本問題的方法,由於小編剛入門所以不能給大家提供更多的解釋,只提出解決方

angular學習(六)—— 依賴注入

依賴注入 依賴注入(DI)是一種處理元件如何獲得依賴的軟體設計模式,在Angular中,injector子系統負責建立元件,解決元件的依賴,並將它們提供給其他元件。 使用依賴注入 在Angular中DI是無處不在的,你可以用它來定義一個元件,也可以提供

Angular 中間部分 2.2 依賴注入和Http

依賴注入 DI (Dependency Injection) system 依賴:當模組a需要模組b才能執行時,模組b是模組a的依賴。 p192, p169 Dependency Injection Parts 註冊一個依賴時,需要繫結到sth識別這

Effective Java 第三版讀書筆記——條款5使用依賴注入替代替代硬連線資源

許多類都會依賴一個或多個基本資源。例如,拼寫檢查器依賴於字典。下面是兩種錯誤的實現方式: 使用 static utility classes: // Inappropriate use of static utility - inflexible & untestable! public cl

Spring4 -03 -Dependency Injection (依賴注入) 程式碼體現/配置xml/測試

DI:中文名稱:依賴注入 英文名稱((Dependency Injection) DI 是什麼?     3.1 DI 和IoC 是一樣的,差不多一樣的技術和模板!     3.2 當一個類(A)中需要依賴另一個類(B)物件時,把 B&n

第1章spring注入/1.2 依賴注入/1.2.1 setting注入/1.2.1.1 概念

易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/

dagger2 讓你愛不釋手基礎依賴注入框架篇

前言 dagger2的大名我想大家都已經很熟了,它是解決Android或java中依賴注入的一個類庫(DI類庫)。當我看到一些開源的專案在使用dagger2時,我也有種匆匆欲動的感覺,因此就立馬想一探它的究竟,到底能給我帶來怎樣的好處。在學習使用dagger2的過程中,我遇到了

Spring的核心機制依賴注入及設值注入和構造注入的區別

Ø 理解依賴注入 在分析原理之前我們先回顧下依賴注入的概念:我們常提起的依賴注入(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。具體含義是:當某個角色(可能是一個Java例項,呼叫者)需要另一個角色(另一個Jav

讀後感分析依賴注入、服務容器的產生

名詞: 依賴注入:依賴Dependency 注入Injection 簡稱DI 控制反轉:反轉Inversion of Control 簡稱IoC 容器:Container 在 Laravel 5 中,IoC 容器改名為服務容器,IoC 容器和服務容器指代同一個東西。 原文地址:Larav

【SSH三大框架】Spring基礎第二篇Spring依賴注入的三種方式

控制反轉(Inversion of Control)和依賴注入(Dependency Injection):應用控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中。所以,控制反轉是,關於一個物件如何獲

Android 依賴注入 Dagger 2 例項講解(一)

本文原創,轉載請註明出處:http://blog.csdn.net/zjbpku 關於Dagger,在之前的博文(Android 依賴注入:Dagger 例項講解(Demo下載))中已有介紹, 本文說的Dagger 2主要是由Google技術 人員參與開發的,當然包括Sq

[ASP.NET Core 3框架揭祕] 依賴注入控制反轉

ASP.NET Core框架建立在一些核心的基礎框架之上,這些基礎框架包括依賴注入、檔案系統、配置選項和診斷日誌等。這些框架不僅僅是支撐ASP.NET Core框架的基礎,我們在進行應用開發的時候同樣會頻繁地使用到它們。對於這裡提到的這幾個基礎框架,依賴注入尤為重要。ASP.NET Core應用在啟動以及後續

[ASP.NET Core 3框架揭祕] 依賴注入IoC模式

正如我們在《依賴注入:控制反轉》提到過的,很多人將IoC理解為一種“面向物件的設計模式”,實際上IoC不僅與面向物件沒有必然的聯絡,它自身甚至算不上是一種設計模式。一般來講,設計模式提供了一種解決某種具體問題的方案,但是IoC既沒有一個針對性的問題領域,其自身也沒有提供一種可操作性的解

[ASP.NET Core 3框架揭祕] 依賴注入依賴注入模式

IoC主要體現了這樣一種設計思想:通過將一組通用流程的控制權從應用轉移到框架之中以實現對流程的複用,並按照“好萊塢法則”實現應用程式的程式碼與框架之間的互動。我們可以採用若干設計模式以不同的方式實現IoC,比如我們在前面介紹的模板方法、工廠方法和抽象工廠,接下來我們介紹一種更有價值的I