中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Angular 2 Forward Reference

發布時間:2020-07-22 02:03:57 來源:網絡 閱讀:437 作者:semlinker 欄目:開發技術

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

@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]

若運行上面的例子,將會拋出以下異常:

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

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

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

我們發現 Buffer 類最終轉成 ES5 中的函數表達式。我們也知道,JavaScript VM 在執行 JS 代碼時,會有兩個步驟,首先會先進行編譯,然后才開始執行。編譯階段,變量聲明和函數聲明會自動提升,而函數表達式不會自動提升。了解完這些后,問題原因一下子明朗了。

那么要解決上面的問題,最簡單的處理方式是交換類定義的順序。除此之外,我們還可以使用 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) { }
}

問題來了,出現上面的問題,我交互個順序不就完了,為什么還要如此大費周章 ?話雖如此,但這樣增加了開發者的負擔,要時刻警惕類定義的順序,特別當一個 ts 文件內包含多個內部類的時候。所以更好地方式還是通過 forwardRef 來解決問題,下面我們就來進一步揭開 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;  
      }
}

通過源碼可以看出,當調用 forwardRef 方法時,我們只是在 forwardRefFn 函數對象上,增加了一個私有屬性__forward_ref__,同時覆寫了函數對象的 toString 方法。在上面代碼中,我們還發現了resolveForwardRef  函數,通過函數名和注釋信息,我們很清楚地了解到,該函數是用來解析通過 forwardRef 包裝過的引用值。

那么 resolveForwardRef 這個函數是由誰負責調用,又是什么時候調用呢 ?其實 resolveForwardRef  這個函數由 Angular 2 的依賴注入系統調用,當解析 Provider 和創建依賴對象的時候,會自動調用該函數。

// @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);  
 }
}
/*************************************************************************/

/** * 構造依賴對象 */
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 解釋器不自動提升 class ?

因為當 class 使用 extends 關鍵字實現繼承的時候,我們不能確保所繼承父類的有效性,那么就可能導致一些無法預知的行為。

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

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

以上代碼能夠正常的輸出 moving,因為 JavaScript 解釋器把會把代碼轉化為:

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

class Dog extends Animal { }

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

然而,當我們把 Animal 轉化為函數表達式,而不是函數聲明的時候:

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

此時以上代碼將會轉化為:

let Animal, defaultMove, dog;

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

class Dog extends Animal 被解釋執行的時候,此時 Animal 的值是 undefined,這樣就會拋出異常。我們可以簡單地通過調整 Animal 函數表達式的位置,來解決上述問題。

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

class Dog extends Animal{}

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

假設 class 也會自動提升的話,上面的代碼將被轉化為以下代碼:

let Animal, defaultMove, dog;

class Dog extends Animal{ }

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

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

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

金寨县| 冀州市| 南江县| 宁河县| 监利县| 巴塘县| 万源市| 盈江县| 尼玛县| 大渡口区| 巫溪县| 衡东县| 婺源县| 星子县| 北京市| 唐海县| 南木林县| 孟州市| 阳泉市| 米林县| 新绛县| 道孚县| 天祝| 贵阳市| 深泽县| 南汇区| 勃利县| 策勒县| 聂荣县| 开阳县| 云阳县| 嘉鱼县| 丹江口市| 鹤峰县| 慈溪市| 平顶山市| 博爱县| 政和县| 泗洪县| 大丰市| 改则县|