溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Angular 2 Forward Reference

發(fā)布時(shí)間:2020-07-22 02:03:57 來源:網(wǎng)絡(luò) 閱讀:437 作者:semlinker 欄目:開發(fā)技術(shù)

Angular 2 通過引入 forwardRef 讓我們可以在使用構(gòu)造注入時(shí),使用尚未定義的依賴對(duì)象類型。下面我們先看一下如果沒有使用 forwardRef ,在開發(fā)中可能會(huì)遇到的問題:

@Injectable()class Socket {  
    constructor(private buffer: Buffer) { }
}

console.log(Buffer); // undefined

@Injectable()class Buffer {  
    constructor(@Inject(BUFFER_SIZE) private size: Number) { }
}

console.log(Buffer); // [Function: Buffer]

若運(yùn)行上面的例子,將會(huì)拋出以下異常:

Error: Cannot resolve all parameters for Socket(undefined).Make sure
they all have valid type or annotations

為什么會(huì)出現(xiàn)這個(gè)問題 ?在探究產(chǎn)生問題的具體原因時(shí),我們要先明白一點(diǎn)。不管我們是使用開發(fā)語言是 ES6、ES7 還是 TypeScript,最終我們都得轉(zhuǎn)換成 ES5 的代碼。然而在 ES5 中是沒有 Class ,只有 Function 對(duì)象。這樣一來,我們的解決問題的思路就是先看一下 Socket 類轉(zhuǎn)換后的 ES5 代碼:

var Buffer = (function () {   
 function Buffer(size) {
    this.size = size;    
 }    
 return Buffer;
}());

我們發(fā)現(xiàn) Buffer 類最終轉(zhuǎn)成 ES5 中的函數(shù)表達(dá)式。我們也知道,JavaScript VM 在執(zhí)行 JS 代碼時(shí),會(huì)有兩個(gè)步驟,首先會(huì)先進(jìn)行編譯,然后才開始執(zhí)行。編譯階段,變量聲明和函數(shù)聲明會(huì)自動(dòng)提升,而函數(shù)表達(dá)式不會(huì)自動(dòng)提升。了解完這些后,問題原因一下子明朗了。

那么要解決上面的問題,最簡單的處理方式是交換類定義的順序。除此之外,我們還可以使用 Angular2 提供的 forward reference 特性來解決問題,具體如下:

import { forwardRef } from'@angular2/core';

@Injectable()
class Socket {  
    constructor(@Inject(forwardRef(() => Buffer)) private buffer) { }
}

@Injectable()
class Buffer {  
    constructor(@Inject(BUFFER_SIZE) private size: Number) { }
}

問題來了,出現(xiàn)上面的問題,我交互個(gè)順序不就完了,為什么還要如此大費(fèi)周章 ?話雖如此,但這樣增加了開發(fā)者的負(fù)擔(dān),要時(shí)刻警惕類定義的順序,特別當(dāng)一個(gè) ts 文件內(nèi)包含多個(gè)內(nèi)部類的時(shí)候。所以更好地方式還是通過 forwardRef 來解決問題,下面我們就來進(jìn)一步揭開 forwardRef 的神秘面紗。

forwardRef 原理分析

// @angular/core/src/di/forward_ref.ts
/** * Allows to refer to references which are not yet defined. */

export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {  
    // forwardRefFn: () => Buffer  
    (<any>forwardRefFn).__forward_ref__ = forwardRef; 
    (<any>forwardRefFn).toString = function() { 
        return stringify(this()); 
    };  
    return (<Type<any>><any>forwardRefFn);
 }
 
 /** * Lazily retrieves the reference value from a forwardRef. */
 export function resolveForwardRef(type: any): any {  
     if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__')
       && type.__forward_ref__ === forwardRef) { 
          return (<ForwardRefFn>type)(); // Call forwardRefFn get Buffer   }
      else {    
          return type;  
      }
}

通過源碼可以看出,當(dāng)調(diào)用 forwardRef 方法時(shí),我們只是在 forwardRefFn 函數(shù)對(duì)象上,增加了一個(gè)私有屬性__forward_ref__,同時(shí)覆寫了函數(shù)對(duì)象的 toString 方法。在上面代碼中,我們還發(fā)現(xiàn)了resolveForwardRef  函數(shù),通過函數(shù)名和注釋信息,我們很清楚地了解到,該函數(shù)是用來解析通過 forwardRef 包裝過的引用值。

那么 resolveForwardRef 這個(gè)函數(shù)是由誰負(fù)責(zé)調(diào)用,又是什么時(shí)候調(diào)用呢 ?其實(shí) resolveForwardRef  這個(gè)函數(shù)由 Angular 2 的依賴注入系統(tǒng)調(diào)用,當(dāng)解析 Provider 和創(chuàng)建依賴對(duì)象的時(shí)候,會(huì)自動(dòng)調(diào)用該函數(shù)。

// @angular/core/src/di/reflective_provider.ts
/** * 解析Provider */
function resolveReflectiveFactory(provider: NormalizedProvider): 
    ResolvedReflectiveFactory {  
        let factoryFn: Function;  
        let resolvedDeps: ReflectiveDependency[];  ...  
        if (provider.useClass) { 
            const useClass = resolveForwardRef(provider.useClass);
            factoryFn = reflector.factory(useClass);
            resolvedDeps = _dependenciesFor(useClass);  
 }
}
/*************************************************************************/

/** * 構(gòu)造依賴對(duì)象 */
export function constructDependencies(    
    typeOrFunc: any, dependencies: any[]): ReflectiveDependency[] {  
    if (!dependencies) {   
      return _dependenciesFor(typeOrFunc);  
     } else { 
         const params: any[][] = dependencies.map(t => [t]);
             return dependencies.map(t => _extractToken(typeOrFunc, t, params)); 
         }
      }
      
/** * 抽取Token */      
function _extractToken(  typeOrFunc: any, metadata: any[] | any,
     params: any[][]): ReflectiveDependency { 
     token = resolveForwardRef(token);  
     if (token != null) {    
         return _createDependency(token, optional, visibility); 
     } else { 
         throw noAnnotationError(typeOrFunc, params);  
     }
}

我有話說

1.為什么 JavaScript 解釋器不自動(dòng)提升 class ?

因?yàn)楫?dāng) class 使用 extends 關(guān)鍵字實(shí)現(xiàn)繼承的時(shí)候,我們不能確保所繼承父類的有效性,那么就可能導(dǎo)致一些無法預(yù)知的行為。

class Dog extends Animal {}function Animal {  
    this.move = function() { 
        alert(defaultMove);  
    }
}

let defaultMove = "moving";
let dog = new Dog();
dog.move();

以上代碼能夠正常的輸出 moving,因?yàn)?JavaScript 解釋器把會(huì)把代碼轉(zhuǎn)化為:

let defaultMove,dog;
function Animal {  this.move = function() { 
    alert(defaultMove);  
  }
}

class Dog extends Animal { }

defaultMove = "moving";
dog = new Dog();
dog.move();

然而,當(dāng)我們把 Animal 轉(zhuǎn)化為函數(shù)表達(dá)式,而不是函數(shù)聲明的時(shí)候:

class Dog extends Animal {}
let Animal = function () {  
    this.move = function () { 
        alert(defaultMove);  
    }
}
let defaultMove = "moving";
let dog = new Dog();
dog.move();

此時(shí)以上代碼將會(huì)轉(zhuǎn)化為:

let Animal, defaultMove, dog;

class Dog extends Animal { }
Animal = function () {  
    this.move = function () { alert(defaultMove);  
    }
}
defaultMove = "moving";
dog = new Dog();
dog.move();

當(dāng) class Dog extends Animal 被解釋執(zhí)行的時(shí)候,此時(shí) Animal 的值是 undefined,這樣就會(huì)拋出異常。我們可以簡單地通過調(diào)整 Animal 函數(shù)表達(dá)式的位置,來解決上述問題。

let Animal = function () { 
     this.move = function () {
        alert(defaultMove);  
     }
}

class Dog extends Animal{}

let defaultMove = "moving";
let dog = new Dog();
dog.move();

假設(shè) class 也會(huì)自動(dòng)提升的話,上面的代碼將被轉(zhuǎn)化為以下代碼:

let Animal, defaultMove, dog;

class Dog extends Animal{ }

Animal = function () { 
     this.move =  function () { 
        alert(defaultMove);  
     }
}

defaultMove = "moving";
dog = new Dog();
dog.move();

此時(shí) Dog 被提升了,當(dāng)解釋器執(zhí)行 extends Animal 語句的時(shí)候,此時(shí)的 Animal 仍然是 undefined,同樣會(huì)拋出異常。所以 ES6 中的 Class 不會(huì)自動(dòng)提升,主要還是為了解決繼承父類時(shí),父類不可用的問題。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI