您好,登錄后才能下訂單哦!
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
不會自動提升,主要還是為了解決繼承父類時,父類不可用的問題。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。