根本原因是编译时未正确导出符号,WASM模块无运行时反射能力,JS只能访问明确标记导出的函数;Rust需用#[no_mangle]和pub extern "C",Emscripten需加-s EXPORTED_FUNCTIONS且函数名带下划线前缀,MIME类型错误或内存引用过期也会导致调用失败。

WASM 导出函数在 JS 中调用失败,instance.exports.my_func 是 undefined
根本原因几乎总是编译时未正确导出符号。WASM 模块本身不带运行时反射能力,JS 只能访问明确标记为导出的函数。
常见错误点:
- Rust 里漏写
#[no_mangle]或没用pub extern "C"声明,导致函数名被 mangling(如变成_ZN3app7add_one17habc123...) - Emscripten 编译时没加
-s EXPORTED_FUNCTIONS='["_add"]',且函数名必须带下划线前缀(_add而非add) - 用
emrun或本地 server 加载时,WASM 文件 MIME 类型返回错(应为application/wasm),浏览器静默忽略导出
验证方式:用 WebAssembly.validate(bytes) 确保字节合法;再打印 Object.keys(instance.exports) 看实际有哪些键。
从 WASM 向 JS 返回字符串,为什么 String.fromCharCode(...) 出乱码?
WASM 没有字符串类型,所有文本都存在线性内存中,且默认是 UTF-8 编码。直接用 String.fromCharCode() 会把每个字节当单字节字符处理,UTF-8 多字节序列立刻崩坏。
立即学习“Java免费学习笔记(深入)”;
正确流程必须分三步:
- WASM 函数先返回两个值:
ptr(起始地址i32)和len(有效字节数,不能依赖\0) - JS 用
new Uint8Array(memory.buffer, ptr, len)构造视图(注意:不是memory.buffer.slice(),那会复制) - 用
new TextDecoder().decode(view)解码,不指定编码时默认 UTF-8
示例片段:
const ptr = instance.exports.get_msg_ptr(); const len = instance.exports.get_msg_len(); const view = new Uint8Array(instance.exports.memory.buffer, ptr, len); const str = new TextDecoder().decode(view);
想让 WASM 发起 HTTP 请求,为什么在 Rust/C 里写 fetch 直接报错?
WASM 标准层完全不提供网络、DOM、文件等系统能力。所谓“WASM 发请求”,本质是 JS 提供回调函数,WASM 主动调用它——这是唯一合规路径。
关键配置缺一不可:
- JS 实例化时通过
imports传入函数,例如:{ env: { js_fetch: (url_ptr, url_len) => { /* fetch 后写回 WASM 内存 */ } } } - Rust 中声明外部函数:
extern "C" { fn js_fetch(ptr: i32, len: i32); },然后在业务逻辑里调用它 - JS 的
js_fetch必须把响应体写入 WASM 内存(用Uint8Array视图),再调用 WASM 的完成通知函数(如on_fetch_done)
绕过这层桥接、试图在 WASM 里硬连网络,链接阶段就会失败(unresolved import)或运行时报 trap。
WebAssembly.Memory 被多次创建或共享时,JS 读内存为什么越界或读到旧数据?
WASM 内存是独立的线性空间,JS 通过 instance.exports.memory 访问。但多个模块、多次实例化、甚至热更新后,这个引用可能已失效。
最容易被忽略的细节:
- 不要缓存
memory.buffer引用——它可能随grow()重分配,旧引用变 stale - 每次读写前,务必用当前
instance.exports.memory重新构造Uint8Array视图 - 若使用 Emscripten 生成的 glue code(
.js+.wasm),优先用其封装好的HEAPU8等全局视图,它们会自动同步
一句话:WASM 内存不是静态快照,JS 侧所有内存操作都得“按需取新”。



















