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 中可调用时,也会存在相同的要求。流入和流出此回调的值必须是兼容的互操作类型或原始类型。
如果您使用 Dart 原始类型(如 String
),编译器会进行隐式转换,将该值从 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 互操作类型的运行时检查更容易避免。有关更多详细信息,请参阅问题 #4841。
null
与 undefined
#JS 同时拥有 null
和 undefined
值。这与 Dart 不同,Dart 只有 null
。为了使 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.5.3。页面最后更新于 2024-08-04。 查看源代码 或 报告问题.