Appearance
第 5 章:与 JavaScript 的双向交互
5.1 导出函数与调用方式
WebAssembly 模块可以通过 export
将函数暴露给 JavaScript,从而在 JS 中直接调用这些函数。以 AssemblyScript 或 Rust 为例,都可以通过特定语法将函数导出。
例如,在 AssemblyScript 中:
ts
// AssemblyScript
export function add(a: i32, b: i32): i32 {
return a + b;
}
然后在 JavaScript 中加载并调用该函数:
js
const wasm = await WebAssembly.instantiateStreaming(fetch('example.wasm'));
console.log(wasm.instance.exports.add(2, 3)); // 输出 5
Rust 中使用 #[wasm_bindgen]
:
rust
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
导出的函数会出现在 JavaScript 的 wasm_bindgen
包装器中。
5.2 从 WASM 调用 JS 函数
WebAssembly 模块也可以调用 JavaScript 中定义的函数。这需要在导入对象中显式声明 JS 函数供 WebAssembly 使用。
JavaScript 侧定义函数并传入:
js
const importObject = {
env: {
log: (value) => console.log("WASM Log:", value)
}
};
const wasm = await WebAssembly.instantiateStreaming(fetch('example.wasm'), importObject);
AssemblyScript 中声明外部导入函数:
ts
@external("env", "log")
declare function log(value: i32): void;
export function testLog(): void {
log(42);
}
这样就可以在 WASM 中调用外部 JS 函数,实现双向通信。
5.3 数据传递与内存管理
WASM 模块与 JavaScript 之间的数据传递需要注意数据类型的兼容性和内存分配方式。基本类型如整数、浮点数可以直接传递,但复杂类型(如字符串、对象)则需要通过共享内存或传递指针的方式。
传递字符串的常见方式:
- 在 WASM 中申请一段内存用于字符串;
- 将字符串编码为 UTF-8 写入该内存;
- 将指针和长度传给 JS;
- JS 使用
TextDecoder
解码内存内容。
例如在 JavaScript 中读取 WASM 传来的字符串:
js
function getStringFromMemory(ptr, len, memory) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
return new TextDecoder('utf8').decode(bytes);
}
内存管理建议:
- 避免频繁分配和释放内存,可考虑使用对象池;
- 使用
wasm-bindgen
时,它会自动处理一些复杂的内存操作; - 注意使用
__alloc
,__free
(AssemblyScript)或手动管理内存(裸 WASM)防止内存泄漏。
通过以上方式,WebAssembly 与 JavaScript 实现了高效且安全的双向交互,为构建高性能 Web 应用提供强大支持。