Kotlin 语言参考文档 中文版 Help

在 Kotlin 中使用 JavaScript 代码

Kotlin 设计时最优先重视与 Java 平台交互的问题: Kotlin 代码可以将 Java 类当作 Kotlin 类来使用, Java 代码也可以将 Kotlin 类当作 Java 类来使用.

然而, JavaScript 是一种动态类型的语言, 也就是说它在编译时刻不做类型检查. 通过 动态类型, 在 Kotlin 中你可以自由地与 JavaScript 交互, 如果你希望完全发挥 Kotlin 类型系统的能力, 你可以为 JavaScript 库创建外部声明, Kotlin 编译器及相关工具能够正确处理这些外部声明.

内联 JavaScript

使用 js() 函数, 你可以将 JavaScript 代码内联到你的 Kotlin 代码中:

fun jsTypeOf(o: Any): String { return js("typeof o") }

JavaScript 代码内联功能完全支持 ES2015 的全部功能, 包括:

  • constlet 变量声明

  • ES 类

  • 生成器(Generator)

  • Lambda (箭头函数)

  • 展开(Spread)和剩余(Rest) 操作符

  • 模版字符串

由于 js 函数的参数会在编译时解析, 然后原样的("as-is")翻译为 JavaScript 代码, 因此参数必须是字符串常数. 所以, 以下代码是不正确的:

fun jsTypeOf(o: Any): String { return js(getTypeof() + " o") // 错误: 参数必须是字符串常数 // 编译器无法计算字符串拼接操作 } fun getTypeof() = "typeof"

相反, 例如要内联剩余(Rest) 操作符, 请使用字符串常数:

fun runSumExample() { val sum = js("(...nums) => nums.reduce((a, b) => a + b, 0)") println(sum(1, 2, 3, 4)) }

external 修饰符

你可以对某个声明使用 external 修饰符, 来告诉 Kotlin 它是由纯 JavaScript 编写的. 编译器看到这样的声明后, 它会假定对应的类, 函数, 或属性的实现, 会由外部提供 (由开发者提供, 或由 npm 依赖项提供), 因此它不会为这个声明生成 JavaScript 代码. 由于同样的原因, external 声明不能带有 body 部. 比如:

external fun alert(message: Any?): Unit external class Node { val firstChild: Node fun append(child: Node): Node fun removeChild(child: Node): Node // 等等 } external val window: Window

注意, external 修饰符会被内嵌的声明继承下来, 因此, 在示例程序的 Node 类的内部, 在成员函数和属性之前没有添加 external 标记.

external 修饰符只允许用于包级声明. 对于非 external 的类, 不允许声明 external 的成员.

声明类的(静态)成员

在 JavaScript 中, 成员函数可以定义在 prototype 上, 也可以定义在类上:

function MyClass() { ... } MyClass.sharedMember = function() { /* 实现代码 */ }; MyClass.prototype.ownMember = function() { /* 实现代码 */ };

在 Kotlin 中没有这样的语法. 但是, 在 Kotlin 中有 同伴 (companion) 对象. Kotlin 以特殊的方式处理 external 类的同伴对象: 它不是期待一个对象, 而是假设同伴对象的成员在 JavaScript 中是定义在类上的成员函数. 上例中的 MyClass, 在 Kotlin 中可以写为:

external class MyClass { companion object { fun sharedMember() } fun ownMember() }

声明带默认值的参数

如果一个 JavaScript 函数包含带默认值的参数, 那么编写外部声明时请使用 definedExternally. 这个设置会将参数默认值的生成委托给 JavaScript 函数自身:

external fun myFunWithOptionalArgs( x: Int, y: String = definedExternally, z: String = definedExternally )

通过这样的外部声明, 你就可以使用一个必须参数和两个可选参数来调用 myFunWithOptionalArgs 函数, 其中, 可选参数的默认值将由 myFunWithOptionalArgs 函数的 JavaScript 实现负责计算.

扩展 JavaScript 类

你可以很容易地扩展 JavaScript 类, 就好像它们是 Kotlin 类一样. 你只需要定义一个 external open 类, 然后通过一个非 external 类来扩展它. 比如:

open external class Foo { open fun run() fun stop() } class Bar : Foo() { override fun run() { window.alert("Running!") } fun restart() { window.alert("Restarting") } }

但存在以下限制:

  • 如果 external 基类的函数已存在不同参数签名的重载版本, 那么你就不能在后代类中覆盖这个函数.

  • 如果函数包含带默认值的参数, 不能覆盖.

  • external 类不能扩展非 external 类.

external 接口

JavaScript 没有接口的概念. 如果一个函数要求它的参数支持 foobar 两个方法, 你只需要传递一个确实带有这些方法的对象.

在严格检查类型的 Kotlin 语言中, 你可以使用接口来表达这种概念:

external interface HasFooAndBar { fun foo() fun bar() } external fun myFunction(p: HasFooAndBar)

external 接口的另一种典型的使用场景, 是用来描述配置信息对象. 比如:

external interface JQueryAjaxSettings { var async: Boolean var cache: Boolean var complete: (JQueryXHR, String) -> Unit // 等等 } fun JQueryAjaxSettings(): JQueryAjaxSettings = js("{}") external class JQuery { companion object { fun get(settings: JQueryAjaxSettings): JQueryXHR } } fun sendQuery() { JQuery.get(JQueryAjaxSettings().apply { complete = { (xhr, data) -> window.alert("Request complete") } }) }

external 接口存在一些限制:

  • 它们不可以用在 is 检查语句的右侧.

  • 它们不可以用作实体化的类型参数(reified type argument).

  • 它们不可以用在类的字面值表达式中(比如 I::class).

  • 使用 as 将对象转换为 external 接口, 永远会成功. 转换为 external 接口会产生编译期警告 "Unchecked cast to external interface". 要消除这个警告, 可以使用 @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") 注解.

    IntelliJ IDEA 也能够自动生成 @Suppress 注解. 方法是在编辑器内点击灯泡图标, 或按下 Alt-Enter 快捷键, 打开 intentions 菜单, 然后点击代码检查信息 "Unchecked cast to external interface" 旁的小箭头. 在这里, 你可以选择对这个警告进行屏蔽的适用范围, 然后 IDE 会在你的源代码文件中添加对应的注解.

类型转换

"不安全的" 类型转换操作符 as 在转换失败时会抛出 ClassCastException 异常, 除此之外, Kotlin/JS 还提供了 unsafeCast<T>(). 如果使用 unsafeCast, 在运行时 完全不进行任何类型检查. 比如, 对于下面两个方法:

fun usingUnsafeCast(s: Any) = s.unsafeCast<String>() fun usingAsOperator(s: Any) = s as String

对应的编译结果分别是:

function usingUnsafeCast(s) { return s; } function usingAsOperator(s) { var tmp$; return typeof (tmp$ = s) === 'string' ? tmp$ : throwCCE(); }

相等判断

与其他平台相比, Kotlin/JS 的相等判断语义有所不同.

在 Kotlin/JS 中, Kotlin 引用相等 操作符 (===) 永远会翻译为 JavaScript 的 严格相等 操作符 (===).

JavaScript === 操作符不仅检查两个值相等, 而且检查这两个值的类型也相等:

fun main() { val name = "kotlin" val value1 = name.substring(0, 1) val value2 = name.substring(0, 1) println(value1 === value2) // 在 Kotlin/JS 平台, 输出结果为 'true' // 在其他平台, 输出结果为 'false' }

而且, 在 Kotlin/JS 中, 数值类型 Byte, Short, Int, Float, 和 Double 在运行期都使用 JavaScript 类型 Number 表达. 因此, 这 5 种类型的值是无法区分的:

fun main() { println(1.0 as Any === 1 as Any) // 在 Kotlin/JS 平台, 输出结果为 'true' // 在其他平台, 输出结果为 'false' }
2026/07/11