JS 類型
Dart 值和 JS 值屬於不同的語言領域。當編譯成 Wasm 時,它們也會在不同的執行階段執行。因此,您應該將 JS 值視為外部類型。為了為 JS 值提供 Dart 類型,dart:js_interop
公開了一組以 JS
為字首的類型,稱為「JS 類型」。這些類型用於在編譯時區分 Dart 值和 JS 值。
重要的是,這些類型的具體化方式會根據您編譯成 Wasm 或 JS 而有所不同。這表示它們的執行階段類型會不同,因此您無法使用 is
檢查和 as
轉換。為了與這些 JS 值互動並檢查它們,您應該使用 external
互通成員或轉換。
類型階層
#JS 類型形成自然的類型階層
- 頂層類型:
JSAny
,這是任何非空值的 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 可能會新增 Lint 以使使用 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;
如果返回型別未宣告為可為空,則如果返回的值是 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.6.0。頁面上次更新時間為 2024-11-17。 檢視原始碼 或 回報問題。