导出 WebAssembly 函数

导出 WebAssembly 函数是 WebAssembly 函数在 JavaScript 中的表示方式。本文将更详细地介绍它们是什么。

导出... 什么?

导出 WebAssembly 函数本质上只是 JavaScript 包装器,它们在 JavaScript 中代表 WebAssembly 函数。当您调用它们时,后台会进行一些活动将参数转换为 Wasm 可以处理的类型(例如,将 JavaScript 数字转换为 Int32),参数被传递到 Wasm 模块内的函数,函数被调用,结果被转换并传递回 JavaScript。

您可以通过两种方式检索导出的 WebAssembly 函数

无论哪种方式,您都将获得对底层函数的相同类型的包装器。从 JavaScript 的角度来看,就好像每个 Wasm 函数也是一个 JavaScript 函数一样——但是它们被导出的 Wasm 函数对象实例封装起来,并且只有有限的方法可以访问它们。

一个示例

让我们看一个示例来澄清(您可以在 GitHub 上找到它,网址为 table-set.html;也可以查看 实时运行的示例,并查看 Wasm 文本表示

js
const otherTable = new WebAssembly.Table({ element: "anyfunc", initial: 2 });

WebAssembly.instantiateStreaming(fetch("table.wasm")).then((obj) => {
  const tbl = obj.instance.exports.tbl;
  console.log(tbl.get(0)()); // 13
  console.log(tbl.get(1)()); // 42
  otherTable.set(0, tbl.get(0));
  otherTable.set(1, tbl.get(1));
  console.log(otherTable.get(0)());
  console.log(otherTable.get(1)());
});

在这里,我们使用 WebAssembly.Table 构造函数从 JavaScript 创建一个表 (otherTable),然后我们使用 WebAssembly.instantiateStreaming() 方法将 table.wasm 加载到我们的页面中。

然后,我们获取从模块导出的函数,通过 tbl.get() 检索它引用的函数,并将每个函数调用的结果记录到控制台。接下来,我们使用 set() 使 otherTable 表包含与 tbl 表相同的函数引用。

为了证明这一点,我们然后从 otherTable 检索这些引用,并将它们的结果也打印到控制台,这将给出相同的结果。

它们是真正的函数

在前面的示例中,每个 Table.prototype.get() 调用的返回值都是一个导出的 WebAssembly 函数——这正是我们一直在讨论的内容。

值得注意的是,除了作为 WebAssembly 函数的包装器之外,它们还是真正的 JavaScript 函数。如果您在支持 WebAssembly 的浏览器 中加载上述示例,并在您的控制台中运行以下代码行

js
const testFunc = otherTable.get(0);
typeof testFunc;

您将获得返回的 function 结果。然后,您可以对该函数执行几乎所有您可以对 JavaScript 中的其他 函数 执行的操作——call()bind() 等等。testFunc.toString() 返回一个有趣的结果

function 0() {
    [native code]
}

这可以让您更了解其包装器的性质。

关于导出的 WebAssembly 函数,还有一些其他需要注意的细节

  • 它们的 length 属性是 Wasm 函数签名中声明的参数数量。
  • 它们的 name 属性是 Wasm 模块中函数索引的 toString() 结果。
  • 如果您尝试调用一个采用或返回 i64 类型值的导出 Wasm 函数,它目前会抛出错误,因为 JavaScript 目前没有精确的方法来表示 i64。解决方案是使用 BigInt 值,它们表示任意大小的整数,因此它们可以正确表示 64 位整数。