如何模擬 JavaScript 互通物件
在本教學中,您將學習如何模擬 JS 物件,以便您可以測試互通實例成員,而無需使用真正的實作。
背景和動機
#在 Dart 中模擬類別通常是透過覆寫實例成員來完成的。然而,由於擴充類型用於宣告互通類型,因此所有擴充類型成員都是靜態調度的,因此無法使用覆寫。這個限制對於擴充成員也成立,因此無法模擬實例擴充類型或擴充成員。
雖然這適用於任何非 external
擴充類型成員,但 external
互通成員很特別,因為它們會叫用 JS 值上的成員。
extension type Date(JSObject _) implements JSObject {
external int getDay();
}
如用法一節中所討論,呼叫 getDay()
將會導致在 JS 物件上呼叫 getDay()
。因此,透過使用不同的 JSObject
,可以呼叫 getDay
的不同實作。
為了做到這一點,應該有一些機制來建立一個具有屬性 getDay
的 JS 物件,該屬性在被呼叫時會呼叫 Dart 函式。一個簡單的方法是建立一個 JS 物件,並將屬性 getDay
設定為轉換後的回呼,例如:
final date = Date(JSObject());
date['getDay'] = (() => 0).toJS;
雖然這可行,但它容易出錯,並且在使用許多互通成員時無法很好地擴展。它也不能正確處理 getter 或 setter。相反地,您應該使用createJSInteropWrapper
和@JSExport
的組合來宣告一個類型,該類型為所有 external
實例成員提供實作。
模擬範例
#import 'dart:js_interop';
import 'package:expect/minitest.dart';
// The Dart class must have `@JSExport` on it or at least one of its instance
// members.
@JSExport()
class FakeCounter {
int value = 0;
@JSExport('increment')
void renamedIncrement() {
value++;
}
void decrement() {
value--;
}
}
extension type Counter(JSObject _) implements JSObject {
external int value;
external void increment();
void decrement() {
value -= 2;
}
}
void main() {
var fakeCounter = FakeCounter();
// Returns a JS object whose properties call the relevant instance members in
// `fakeCounter`.
var counter = createJSInteropWrapper<FakeCounter>(fakeCounter) as Counter;
// Calls `FakeCounter.value`.
expect(counter.value, 0);
// `FakeCounter.renamedIncrement` is renamed to `increment`, so it gets
// called.
counter.increment();
expect(counter.value, 1);
expect(fakeCounter.value, 1);
// Changes in the fake affect the wrapper and vice-versa.
fakeCounter.value = 0;
expect(counter.value, 0);
counter.decrement();
// Because `Counter.decrement` is non-`external`, we never called
// `FakeCounter.decrement`.
expect(counter.value, -2);
}
@JSExport
允許您宣告一個可以在 createJSInteropWrapper
中使用的類別。createJSInteropWrapper
將建立一個物件字面值,將每個類別的實例成員名稱(或重新命名)對應到 JS 回呼,該回呼是使用Function.toJS
建立的。當被呼叫時,JS 回呼會反過來呼叫實例成員。在上面的範例中,取得和設定 counter.value
會取得和設定 fakeCounter.value
。
您可以僅指定類別的某些成員匯出,方法是省略類別的註釋,而是僅註釋特定的成員。您可以在@JSExport
的文件中查看有關更多專業匯出(包括繼承)的更多詳細資訊。
請注意,此機制不僅限於測試。您可以使用它為任意 Dart 物件提供 JS 介面,讓您可以使用預先定義的介面將 Dart 物件匯出到 JS。
除非另有說明,否則本網站上的文件反映 Dart 3.6.0。頁面最後更新於 2024-11-17。 檢視原始碼 或 回報問題。