在 JavaScript 中使用 Kotlin 代码
根据所选的 JavaScript 模块系统,Kotlin/JS 编译器会生成不同的输出。 当然通常 Kotlin 编译器生成正常的 JavaScript 类,可以在 JavaScript 代码中自由地使用的函数和属性。不过,应该记住一些微妙的事情。
在 plain 模式中用独立的 JavaScript 隔离声明
如果将模块种类明确设置为 plain
, 为了防止损坏全局对象,
Kotlin 创建一个包含当前模块中所有 Kotlin 声明的对象。这意味着对于一个模块 myModule
,
所有的声明都可以通过 myModule
对象在 JavaScript 中使用。例如:
fun foo() = "Hello"
可以在 JavaScript 中这样调用:
alert(myModule.foo());
将 Kotlin 模块编译为 JavaScript 模块 〔例如 UMD,(这是
browser
与 nodejs
目标的默认设置)、CommonJS 或 AMD〕时,此方法不适用。在这种情况下,声明将以选择的
JavaScript 模块系统指定的格式暴露。例如,当使用 UMD 或 CommonJS 时,调用处可能如下所示:
alert(require('myModule').foo());
查看有关 JavaScript 模块的文章,以获取有关 JavaScript 模块系统专题的更多信息。
包结构
Kotlin 将其包结构暴露给 JavaScript,因此除非你在根包中定义声明, 否则必须在 JavaScript 中使用完整限定名。例如:
package my.qualified.packagename
fun foo() = "Hello"
例如,当使用 UMD 或 CommonJS 时,调用处可能如下所示:
alert(require('myModule').my.qualified.packagename.foo())
或者,在使用 plain
格式作为模块系统设置的情况下:
alert(myModule.my.qualified.packagename.foo());
@JsName 注解
在某些情况下(例如为了支持重载),Kotlin 编译器会修饰(mangle) JavaScript 代码中生成的函数和属性的名称。要控制生成的名称,可以使用 @JsName
注解:
// 模块“kjs”
class Person(val name: String) {
fun hello() {
println("Hello $name!")
}
@JsName("helloWithGreeting")
fun hello(greeting: String) {
println("$greeting $name!")
}
}
现在,你可以通过以下方式在 JavaScript 中使用这个类:
// 如有必要,根据所选模块系统导入“kjs”
var person = new kjs.Person("Dmitry"); // 引用到模块“kjs”
person.hello(); // 输出“Hello Dmitry!”
person.helloWithGreeting("Servus"); // 输出“Servus Dmitry!”
如果我们没有指定 @JsName
注解,相应函数的名称会包含从函数签名计算而来的后缀,例如 hello_61zpoe$
。
请注意,在某些情况下,Kotlin 编译器不应用修饰:
external
声明不会被修饰- 从
external
类继承的非external
类中的任何重写函数都不会被修饰。
@JsName
的参数需要是一个常量字符串字面值,该字面值是一个有效的标识符。
任何尝试将非标识符字符串传递给 @JsName
时,编译器都会报错。
以下示例会产生编译期错误:
@JsName("new C()") // 此处出错
external fun newC()
@JsExport 注解
@JsExport
注解当前标记为实验性的。其设计可能会在将来的版本中更改。
通过将 @JsExport
注解应用于顶级声明(如类或函数),可以从 JavaScript 使用 Kotlin
声明。注解会导出所有嵌套声明,并使用 Kotlin 中给出的名称。
也可以使用 @file:JsExport
将其应用于文件级。
要解决导出中的歧义(例如,具有相同名称的函数的重载),可以将 @JsExport
批注与 @JsName
一起使用,以指定生成与导出函数的名称。
@JsExport
注解在当前的默认编译器后端与新的 IR 编译器后端中可用。
如果以 IR 编译器后端为目标,则 you must use the @JsExport
annotation to make your functions visible
from Kotlin in the first place.
对于多平台项目,@JsExport
也可以在公共代码中使用。它仅在针对
JavaScript 目标进行编译时才有效,并且还允许导出非平台特有的 Kotlin 声明。
JavaScript 中的 Kotlin 类型
See how Kotlin types are mapped to JavaScript ones:
Kotlin | JavaScript | Comments | ||
---|---|---|---|---|
Byte , Short , Int , Float , Double |
Number |
|||
Char |
Number |
The number represents the character's code. | ||
Long |
Not supported | There is no 64-bit integer number type in JavaScript, so it is emulated by a Kotlin class. | ||
Boolean |
Boolean |
|||
String |
String |
|||
Array |
Array |
|||
ByteArray |
Int8Array |
|||
ShortArray |
Int16Array |
|||
IntArray |
Int32Array |
|||
CharArray |
UInt16Array |
Carries the property $type$ == "CharArray" . |
||
FloatArray |
Float32Array |
|||
DoubleArray |
Float64Array |
|||
LongArray |
Array<kotlin.Long> |
Carries the property $type$ == "LongArray" . Also see Kotlin's Long type comment. |
||
BooleanArray |
Int8Array |
Carries the property $type$ == "BooleanArray" . |
||
Unit |
Undefined | |||
Any |
Object |
|||
Throwable |
Error |
|||
Nullable Type? |
`Type \ | null \ | undefined` | |
All other Kotlin types (except for those marked with JsExport annotation) |
Not supported | Includes Kotlin's collections (List , Set , Map , etc.), and unsigned variants. |
Additionally, it is important to know that:
- Kotlin preserves overflow semantics for
kotlin.Int
,kotlin.Byte
,kotlin.Short
,kotlin.Char
andkotlin.Long
. Kotlin cannot distinguish between numeric types at runtime (except for
kotlin.Long
), so the following code works:fun f() { val x: Int = 23 val y: Any = x println(y as Float) }
Kotlin 在 JavaScript 中保留了惰性对象初始化。
- Kotlin 不会在 JavaScript 中实现顶层属性的惰性初始化。