JS 類型
Dart 值和 JS 值屬於不同的語言領域。當編譯為 Wasm 時,它們也在不同的執行階段中執行。因此,您應該將 JS 值視為外部類型。為了為 JS 值提供 Dart 類型,dart:js_interop
公開了一組以 JS
為前綴的類型,稱為「JS 類型」。這些類型用於在編譯時期區分 Dart 值和 JS 值。
重要的是,這些類型的具體化方式會根據您編譯為 Wasm 或 JS 而有所不同。這表示它們的執行階段類型會有所不同,因此您無法使用 is
檢查和 as
轉型。為了與這些 JS 值互動和檢查,您應該使用 external
互通成員或轉換。
類型階層
#JS 類型形成自然的類型階層
- 頂層類型:
JSAny
,它是任何非 nullish 的 JS 值- 基本類型:
JSNumber
、JSBoolean
、JSString
JSSymbol
JSBigInt
JSObject
,它是任何 JS 物件JSFunction
JSExportedDartFunction
,它表示已轉換為 JS 函式的 Dart 回呼
JSArray
JSPromise
JSDataView
JSTypedArray
- JS 類型化陣列,例如
JSUint8Array
- JS 類型化陣列,例如
JSBoxedDartObject
,它允許使用者在同一個 Dart 執行階段中封裝並以不透明方式傳遞 Dart 值- 從 Dart 3.4 開始,
dart:js_interop
中的ExternalDartReference
類型也允許使用者以不透明方式傳遞 Dart 值,但它不是 JS 類型。請這裡瞭解更多關於每個選項的權衡。
- 從 Dart 3.4 開始,
- 基本類型:
您可以在 dart:js_interop
API 文件中找到每個類型的定義。
轉換
#若要使用來自一個領域的值到另一個領域,您可能需要將該值轉換為另一個領域的對應類型。例如,您可能想要將 Dart List<JSString>
轉換為字串的 JS 陣列,該陣列由 JS 類型 JSArray<JSString>
表示,以便您可以將該陣列傳遞給 JS 互通 API。
Dart 在各種 Dart 類型和 JS 類型上提供了許多轉換成員,以便為您轉換領域之間的值。
將值從 Dart 轉換為 JS 的成員通常以 toJS
開頭
String str = 'hello world';
JSString jsStr = str.toJS;
將值從 JS 轉換為 Dart 的成員通常以 toDart
開頭
JSNumber jsNum = ...;
int integer = jsNum.toDartInt;
並非所有 JS 類型都有轉換,也並非所有 Dart 類型都有轉換。一般來說,轉換表如下所示
dart:js_interop 類型 | Dart 類型 |
---|---|
JSNumber 、JSBoolean 、JSString | num 、int 、double 、bool 、String |
JSExportedDartFunction | Function |
JSArray<T extends JSAny?> | List<T extends JSAny?> |
JSPromise<T extends JSAny?> | Future<T extends JSAny?> |
類型化陣列,例如 JSUint8Array | 來自 dart:typed_data 的類型化列表 |
JSBoxedDartObject | 不透明的 Dart 值 |
ExternalDartReference | 不透明的 Dart 值 |
關於 external
宣告和 Function.toJS
的要求
#為了確保類型安全和一致性,編譯器對可以流入和流出 JS 的類型施加了要求。不允許將任意 Dart 值傳遞到 JS 中。相反地,編譯器要求使用者使用相容的互通類型 ExternalDartReference
或基本類型,然後編譯器會隱式轉換這些類型。例如,以下是被允許的
@JS()
external void primitives(String a, int b, double c, num d, bool e);
@JS()
external JSArray jsTypes(JSObject _, JSString __);
extension type InteropType(JSObject _) implements JSObject {}
@JS()
external InteropType get interopType;
@JS()
external void externalDartReference(ExternalDartReference _);
而以下會傳回錯誤
@JS()
external Function get function;
@JS()
external set list(List _);
當您使用 Function.toJS
使 Dart 函式可在 JS 中調用時,也存在相同的要求。流入和流出此回呼的值必須是相容的互通類型或基本類型。
如果您使用像 String
這樣的 Dart 基本類型,則編譯器會進行隱式轉換,將該值從 JS 值轉換為 Dart 值。如果效能至關重要,且您不需要檢查字串的內容,那麼像第二個範例一樣,使用 JSString
來避免轉換成本可能是有意義的。
相容性、類型檢查和轉型
#JS 類型的執行階段類型可能會因編譯器而異。這會影響執行階段類型檢查和轉型。因此,幾乎總是避免在值是互通類型或目標類型是互通類型的情況下使用 is
檢查
void f(JSAny a) {
if (a is String) { … }
}
void f(JSAny a) {
if (a is JSObject) { … }
}
此外,避免在 Dart 類型和互通類型之間進行轉型
void f(JSString s) {
s as String;
}
若要類型檢查 JS 值,請使用互通成員,例如 typeofEquals
或 instanceOfString
,它們會檢查 JS 值本身
void f(JSAny a) {
// Here `a` is verified to be a JS function, so the cast is okay.
if (a.typeofEquals('function')) {
a as JSFunction;
}
}
從 Dart 3.4 開始,您可以使用 isA
輔助函式來檢查值是否為任何互通類型
void f(JSAny a) {
if (a.isA<JSString>()) {} // `typeofEquals('string')`
if (a.isA<JSArray>()) {} // `instanceOfString('Array')`
if (a.isA<CustomInteropType>()) {} // `instanceOfString('CustomInteropType')`
}
根據類型參數,它會將呼叫轉換為該類型的適當類型檢查。
Dart 可能會新增 lints,以使執行階段檢查 JS 互通類型更容易避免。請參閱 issue #4841 以瞭解更多詳細資訊。
null
與 undefined
#JS 同時具有 null
和 undefined
值。這與僅具有 null
的 Dart 形成對比。為了使 JS 值更符合人體工學地使用,如果互通成員要傳回 JS null
或 undefined
,則編譯器會將這些值對應到 Dart null
。因此,以下範例中的 value
成員可以解釋為傳回 JS 物件、JS null
或 undefined
@JS()
external JSObject? get value;
如果傳回類型未宣告為可為 null,則如果傳回的值是 JS null
或 undefined
,程式將會拋出錯誤,以確保健全性。
JSBoxedDartObject
與 ExternalDartReference
#從 Dart 3.4 開始,JSBoxedDartObject
和 ExternalDartReference
都可用於透過 JavaScript 傳遞 Dart Object
的不透明參考。但是,JSBoxedDartObject
將不透明參考包裝在 JavaScript 物件中,而 ExternalDartReference
是參考本身,因此不是 JS 類型。
如果您需要 JS 類型,或者如果您需要額外的檢查以確保 Dart 值不會傳遞到另一個 Dart 執行階段,請使用 JSBoxedDartObject
。例如,如果 Dart 物件需要放置在 JSArray
中或傳遞到接受 JSAny
的 API,請使用 JSBoxedDartObject
。否則請使用 ExternalDartReference
,因為它會更快。
請參閱 toExternalReference
和 toDartObject
,以轉換為和從 ExternalDartReference
轉換。
除非另有說明,否則本網站上的文件反映 Dart 3.7.1 版本。頁面最後更新於 2025-01-22。 檢視原始碼 或 回報此頁面問題。