国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

angular源碼分析之StaticInjector

zhouzhou / 1602人閱讀

摘要:上一篇說到了平臺實例在初始化的時候會創建根注入器那現在就一起看看注入器是如何創建的又是如何工作的所有引用的代碼都被簡化了創建注入器程序初始化時調用的創建根注入器的靜態方法調用此方法會返回一個類型的實例也就是注入器類注入器的構造函數在初始化過

上一篇說到了平臺實例在初始化的時候會創建根注入器,那現在就一起看看注入器是如何創建的,又是如何工作的.(所有引用的代碼都被簡化了)

創建注入器

程序初始化時調用的創建根注入器的靜態方法:

abstract class Injector{
  static create(options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},parent?: Injector): Injector {
      if (Array.isArray(options)) {
          return new StaticInjector(options, parent);
      } else {
      return new StaticInjector(options.providers, options.parent, options.name || null);
  }
}

調用此方法會返回一個StaticInjector類型的實例(也就是注入器).

StaticInjector

export class StaticInjector implements Injector {
  readonly parent: Injector;
  readonly source: string|null;
  private _records: Map;
  constructor(providers: StaticProvider[], parent: Injector = NULL_INJECTOR, source: string|null = null) {
    this.parent = parent;
    this.source = source;
    const records = this._records = new Map();
    records.set(Injector, {token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
    records.set(INJECTOR, {token: INJECTOR, fn: IDENT, deps: EMPTY, value: this, useNew: false});
    recursivelyProcessProviders(records, providers);
  }
}

注入器的構造函數在初始化過程中的操作:

設置當前注入器的父注入器

設置注入器的源

新建注冊表(_records屬性,是一個Map類型)

將參數providers全部添加到注冊表中

向注冊表中添加服務調用了recursivelyProcessProviders函數

const EMPTY = [];
const MULTI_PROVIDER_FN = function (): any[] { return Array.prototype.slice.call(arguments) };
function recursivelyProcessProviders(records: Map, provider: StaticProvider) {
    if (provider instanceof Array) {
      for (let i = 0; i < provider.length; i++) {
        recursivelyProcessProviders(records, provider[i]);
      }
    } else (provider && typeof provider === "object" && provider.provide) {
      let token = resolveForwardRef(provider.provide);// 方法`resolveForwardRef`的作用可能是向前兼容,可以忽略
      const resolvedProvider = resolveProvider(provider);
      if (provider.multi === true) {
        let multiProvider: Record | undefined = records.get(token);
        if (multiProvider) {
          if (multiProvider.fn !== MULTI_PROVIDER_FN) {
            throw multiProviderMixError(token);
          }
        } else {
          records.set(token, multiProvider = {
            token: provider.provide,
            deps: [],
            useNew: false, // 這個值在后面獲取依賴實例的時候會用到,當做判斷條件
            fn: MULTI_PROVIDER_FN,
            value: EMPTY // 這個值在后面獲取依賴實例的時候會用到,當做判斷條件
          });
        }
        token = provider;
        multiProvider.deps.push({ token, options: OptionFlags.Default });
      }
      records.set(token, resolvedProvider);
    }
}

recursivelyProcessProviders函數具體的執行過程:

如果provider是個數組,那就遍歷后依次調用此方法.

如果provider是個對象:

1 獲取token

  let token = resolveForwardRef(provider.provide);

2 調用resolveProvider方法處理服務中可能出現的屬性和依賴,返回一個Record對象,此對象會作為token的值

function resolveProvider(provider: SupportedProvider): Record {
  const deps = computeDeps(provider);
  let fn: Function = function (value) { return value };
  let value: any = [];
  // useUew用來標識是否需要 new 
  let useNew: boolean = false;
  let provide = resolveForwardRef(provider.provide);
  if (USE_VALUE in provider) {
    value = provider.useValue;
  } else if (provider.useFactory) {
    fn = provider.useFactory;
  } else if (provider.useExisting) {
    //do nothing
  } else if (provider.useClass) {
    useNew = true;
    fn = resolveForwardRef(provider.useClass);
  } else if (typeof provide == "function") {
    useNew = true;
    fn = provide;
  } else {
    throw staticError("StaticProvider does not have [useValue|useFactory|useExisting|useClass] or [provide] is not newable", provider);
  }
  return { deps, fn, useNew, value }; // provider中不同的屬性會返回包含不同值的對象
}

這個方法會先調用computeDeps函數處理服務需要的依賴,它將useExisting類型的服務也轉換成deps,最后返回[{ token, OptionFlags }]形式的數組(OptionFlags是枚舉常量)

  function computeDeps(provider: StaticProvider): DependencyRecord[] {
    let deps: DependencyRecord[] = EMPTY;
    const providerDeps: any[] = provider.deps;
    if (providerDeps && providerDeps.length) {
      deps = [];
      for (let i = 0; i < providerDeps.length; i++) {
        let options = OptionFlags.Default;
        let token = resolveForwardRef(providerDeps[i]);
        deps.push({ token, options });
      }
    } else if ((provider as ExistingProvider).useExisting) {
      const token = resolveForwardRef((provider as ExistingProvider).useExisting);
      deps = [{ token, options: OptionFlags.Default }];
    } 
    return deps;
  }

resolveProvider函數最終返回的Record對象有一個缺省值:

{
  deps:[], // 包含依賴時 [{ token, options },{ token, options }]
  fn:function(value) { return value },
  useNew:false,
  value:[]
}

執行過程中會根據provider不同的屬性修改Record對象的變量為不同的值:

useValue : 修改valueuseValue的值

useFactory : 修改fn為對應的函數

useClasstypeof provide == "function"(令牌為一個函數時) : 修改fn為對應的函數,并設置useNewtrue

useExisting : 不做修改,直接使用默認值

3 如果是多處理服務(multi:ture)且為首次注冊,那么在注冊表中額外注冊一個占位的Record

records.set(token, multiProvider = {
  token: provider.provide,
  deps: [],
  useNew: false,
  fn: MULTI_PROVIDER_FN,
  value: EMPTY
});

4 非多處理服務以token為鍵,多處理服務以provider對象為鍵,返回的Record對象為值,存入注冊表records

從注入器中獲取實例

服務注冊完,下一步就是怎么從注入器中獲取服務的實例了,這會調用StaticInjectorget方法

export class StaticInjector implements Injector {
  get(token: any, notFoundValue?: any, flags: InjectFlags = InjectFlags.Default): any {
    // 獲取token對應的record
    const record = this._records.get(token);
    return resolveToken(token, record, this._records, this.parent, notFoundValue, flags);
}

get方法調用了resolveToken函數,這個函數會返回token對應的實例(就是被注入的對象)

const EMPTY = [];
const CIRCULAR = IDENT;
const IDENT = function (value: T): T { return value };
function resolveToken(token: any, record: Record | undefined, records: Map, parent: Injector,
  notFoundValue: any, flags: InjectFlags): any {
  let value;
  if (record && !(flags & InjectFlags.SkipSelf)) {
    value = record.value;
    if (value == CIRCULAR) {
      throw Error(NO_NEW_LINE + "Circular dependency");
    } else if (value === EMPTY) {
      record.value = CIRCULAR;
      let obj = undefined;
      let useNew = record.useNew;
      let fn = record.fn;
      let depRecords = record.deps;
      let deps = EMPTY;
      if (depRecords.length) {
        deps = [];
        for (let i = 0; i < depRecords.length; i++) {
          const depRecord: DependencyRecord = depRecords[i];
          const options = depRecord.options;
          const childRecord = options & OptionFlags.CheckSelf ? records.get(depRecord.token) : undefined;
          deps.push(tryResolveToken(
            depRecord.token,
            childRecord,
            records,
            !childRecord && !(options & OptionFlags.CheckParent) ? NULL_INJECTOR : parent,
            options & OptionFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND,
            InjectFlags.Default));
        }
      }
      record.value = value = useNew ? new (fn as any)(...deps) : fn.apply(obj, deps);
    }
  } else if (!(flags & InjectFlags.Self)) {
    value = parent.get(token, notFoundValue, InjectFlags.Default);
  }
  return value;
}

函數中會先判斷當前請求的token是否存在,如果不存在則去當前注入器的父注入器中尋找,如果存在:

獲取token對應的record

判斷record.value是否為[](非useValue類型的服務/多處理服務的默認值是[]):

ture : 非useValue類型的服務/多處理服務或此服務沒有被創建過

查看是否包含依賴,包含則優先創建依賴的實例,也是調用這個函數

根據record.fn創建當前token對應的實例并更新record.value(這里需要根據record.useNew來判斷是否需要用new來實例化,比如useFactory類型就不需要new,而useExisting更是直接返回了deps)

返回這個值

false : useValue類型的服務或此服務已經被創建過

直接返回這個值

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100059.html

相關文章

  • Angular 5.0 來了! 有這些大變化

    摘要:以下簡單介紹的重大變化。狀態轉交及對的支持這樣更便于在服務端和客戶之間共享應用狀態。狀態轉交的相關文檔幾周后會發布。我們刪除很多以前廢棄的如,也公布了一些新的廢棄項。以上指南會詳細介紹這些變更。已知問題當前已知與相關的問題。 我們很高興地宣布Angular 5.0.0——五角形甜甜圈發布啦!這又是一個主版本,包含新功能并修復了很多bug。它再次體現了我們把Angular做得更小、更快、...

    張紅新 評論0 收藏0
  • Angular 5.0 來了! 有這些大變化

    摘要:以下簡單介紹的重大變化。狀態轉交及對的支持這樣更便于在服務端和客戶之間共享應用狀態。狀態轉交的相關文檔幾周后會發布。我們刪除很多以前廢棄的如,也公布了一些新的廢棄項。以上指南會詳細介紹這些變更。已知問題當前已知與相關的問題。 我們很高興地宣布Angular 5.0.0——五角形甜甜圈發布啦!這又是一個主版本,包含新功能并修復了很多bug。它再次體現了我們把Angular做得更小、更快、...

    DobbyKim 評論0 收藏0
  • @angular/router 源碼分析注冊路由

    摘要:模塊主要解決程序路由狀態改變和懶加載模塊問題。本文主要解釋程序啟動后,是如何注冊開發者定義的路由集合的,和實例化對象的。第六個重要的對象就是,提供了初始導航功能。 @angular/router 模塊主要解決程序路由狀態改變和懶加載模塊問題。 比如,程序從路由狀態 state1: /advisors/1/households/1 轉變為路由狀態 state2: /advisors/1/...

    hidogs 評論0 收藏0
  • 源碼分析 @angular/cdk Portal

    摘要:這些依賴對象也進一步暴露了其設計思想。關鍵功能包括在上下文內掛載在上下文外掛載在上下文外共享數據。在構造必須依賴,所以可以直接創建嵌入視圖,然后手動強制執行變更檢測。提供了兩個指令和。 @angular/material 是 Angular 官方根據 Material Design 設計語言提供的 UI 庫,開發人員在開發 UI 庫時發現很多 UI 組件有著共同的邏輯,所以他們把這些共...

    BearyChat 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<