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 JSSymbolJSBigIntJSObject,它是任何 JS 对象JSFunctionJSExportedDartFunction,它表示已转换为 JS 函数的 Dart 回调函数
JSArrayJSPromiseJSDataViewJSTypedArray- JS 类型化数组,例如
JSUint8Array
- JS 类型化数组,例如
JSBoxedDartObject,允许用户在同一 Dart 运行时中不透明地打包和传递 Dart 值- 从 Dart 3.4 开始,
dart:js_interop中的ExternalDartReference类型也允许用户不透明地传递 Dart 值,但它不是 JS 类型。在此处[了解]每个选项之间的权衡 here 。
- 从 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) {
// 在这里, `a` 被验证为 JS 函数,因此强制转换是可以的。
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.6.0。页面最后更新于 2025-02-05。 查看源代码 或 报告问题.