news 2026/10/6 2:34:49

wasm-bindgen 中 `getter` 与 `setter` 属性完全指南:从 JavaScript 属性绑定到 Symbol 访问器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
wasm-bindgen 中 `getter` 与 `setter` 属性完全指南:从 JavaScript 属性绑定到 Symbol 访问器
  • 开发工具

【免费下载链接】wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript

项目地址:https://gitcode.com/gh_mirrors/wa/wasm-bindgen
点击查看免费下载

本指南聚焦 wasm-bindgen 的#[wasm_bindgen(method, getter)]与#[wasm_bindgen(method, setter)]属性,讲解如何在 Rust 中声明 JavaScript 类实例属性的读取与写入访问器。你将掌握默认命名规则(getter 同名、setter 的set_前缀)、通过属性参数显式指定属性名、structural与默认"原型链缓存"两种访问模式的差异,以及"[Symbol.<name>]"形式的 well-known symbol 绑定技巧。

一、属性概述:把 JavaScript 属性当作 Rust 方法调用

getter和setter是两个只能与method组合使用的属性(method的完整说明见 method 属性)。它们把 Rust 中导入的函数声明标记为对 JavaScript 对象"属性"的访问器:

  • 带getter的函数,默认访问与Rust 函数同名的 JavaScript 属性;
  • 带setter的函数,函数名必须以set_开头,实际访问的属性名是set_之后的后缀部分。

这一规则由宏解析器在语法层面直接落地:在 crates/macro-support/src/parser.rs 中,getter与setter被解析为Getter(Span, Option<String>)与Setter(Span, Option<String>)两个可带可选字符串参数的属性;随后在 同文件 L3352-L3356 处被转换为ast::OperationKind::Getter/Setter操作类别。

二、默认命名规则与最小可用示例

假设 JavaScript 端有一个对white_russians属性定义 getter/setter 的类:

class TheDude { get white_russians() { ... } set white_russians(val) { ... } }

对应的 Rust 导入写法如下:

#[wasm_bindgen] extern "C" { type TheDude; #[wasm_bindgen(method, getter)] fn white_russians(this: &TheDude) -> u32; #[wasm_bindgen(method, setter)] fn set_white_russians(this: &TheDude, val: u32); }

这里导入了TheDude类型,并定义了访问每个实例white_russians属性的能力:

  • 第一个函数是 getter,在 Rust 中通过the_dude.white_russians()调用(方法式调用);
  • 第二个函数是 setter,通过the_dude.set_white_russians(2)写入值。

注意:由于两个函数都标记了method,因此必须带有this: &TheDude作为第一个参数(详见 method 属性)。

三、显式指定属性名:getter = name/setter = name

getter和setter都可以携带一个字符串参数,用来显式声明实际访问的属性名。一旦显式指定,对 Rust 函数名就不再有任何约束(getter 不再要求同名,setter 不再要求set_前缀):

#[wasm_bindgen] extern "C" { type TheDude; #[wasm_bindgen(method, getter = white_russians)] fn my_custom_getter_name(this: &TheDude) -> u32; #[wasm_bindgen(method, setter = white_russians)] fn my_custom_setter_name(this: &TheDude, val: u32); }

这段代码与上一节完全等价:两个函数虽然在 Rust 中叫my_custom_getter_name/my_custom_setter_name,但访问的都是white_russians属性。这一显式形式也方便同时使用js_name进一步控制 Rust 侧标识符,二者可以自由组合(属性名与js_name的交互规则在测试中有系统验证,见下文)。

四、两种访问模式:默认原型缓存 vsstructural

务必注意:getter/setter函数在加载时只从构造函数的原型链上查找一次,结果被缓存,之后每次访问都直接调用缓存下来的访问器(accessor)。

默认模式下,Rust 调用the_dude.white_russians()实际生成的 JavaScript 等价于:

// 这是默认情况下 Rust 调用 the_dude.white_russians() 时所执行的逻辑: const white_russians = Object.getOwnPropertyDescriptor( TheDude.prototype, "white_russians" ).get;

也就是说,访问器函数指针在模块初始化时就被"钉死"。如果需要在每一次访问时都动态遍历原型链(例如对象被继承、属性被动态覆写,或传入的不是该构造函数的实例),请加上structural属性:

// 这是加上 structural 之后的效果: const white_russians = function(the_dude) { return the_dude.white_russians; };

structural的具体语义见 structural 属性:它表示以"结构化、鸭子类型"的方式访问方法或属性,每次访问都沿原型链实时查找。该文档同时指出,自 [RFC 5] 之后structural已是所有导入函数的默认行为,属性本身目前很大程度上被忽略、仅为向后兼容保留;其反面final属性 反而更具功能意义。因此对getter/setter而言,"原型链缓存"对应的是非structural(即final)语义,加不加structural决定了访问器是静态固化还是动态查找。

源码层面的两种生成路径

这两种模式在编译器中对应完全不同的代码生成分支,可以从 crates/cli-support/src/wit/mod.rs 中清晰地看到:

  • structural模式下,getter 被转译为AuxImport::StructuralGetter(field),setter 被转译为AuxImport::StructuralSetter(field)(静态成员则对应StructuralClassGetter/StructuralClassSetter);
  • 非structural模式下,则生成AuxValue::Getter(class, field)/AuxValue::Setter(class, field)(静态成员对应ClassGetter/ClassSetter),走的是"加载时取访问器"的 Value 管线。

随后在 crates/cli-support/src/js/mod.rs 中,StructuralGetter被内联生成receiver + 属性访问的表达式(严格断言只允许 1 个参数),StructuralSetter则生成receiver + 属性访问 = 新值的赋值表达式(严格断言恰好 2 个参数)。这些断言与文档中"getter 带this、setter 带this和val"的签名约定一一对应。

五、非合法标识符与原型链兜底:编译器如何保证正确性

property_accessor是生成属性访问表达式的核心辅助函数,定义在 crates/cli-support/src/js/mod.rs:

  • 属性名是合法 JS 标识符时生成点号访问,如foo.bar;
  • 属性名不是合法 JS 标识符时自动转为方括号字符串形式,如foo["kebab-case"];
  • 以[开头、以]结尾的名称被视为计算键表达式,原样拼接(这正是"[Symbol.iterator]"这类 Symbol 键的入口,见下文);
  • 点分路径(如"prototype.set.call")会按.拆分后逐段渲染,以保留历史行为。

另外,由于默认模式在加载时要沿原型链查找访问器,而个别浏览器存在"将描述符上移到原型链"的异常行为,编译器内置了一个GetOwnOrInheritedPropertyDescriptor兜底函数,手动遍历原型链定位属性描述符,见 crates/cli-support/src/js/mod.rs。这是默认缓存模式稳定性的底层保障。

六、绑定 well-known symbol 访问器

getter和setter都支持显式的方括号字符串形式"[Symbol.<name>]",用于绑定以 well-known symbol 为键的访问器:

#[wasm_bindgen] extern "C" { #[wasm_bindgen(js_name = String)] type JsString; #[wasm_bindgen(method, js_class = "String", getter = "[Symbol.toPrimitive]")] fn to_primitive(this: &JsString) -> String; }

这段代码绑定的是String.prototype上的[Symbol.toPrimitive]getter(注意同时使用了js_class = "String"指定构造函数;js_name与js_class的配合规则见 js_name 属性)。

关于该语法有三点限制:

  1. 只接受精确形式"[Symbol.<ident>]",[ ... ]内部不支持任意表达式;
  2. 同样的语法也被js_name支持,用于非 getter/setter 的普通导入(例如js_namespace = SomeClass, js_name = "[Symbol.toPrimitive]"的静态调用,或method, js_class = "String", js_name = "[Symbol.iterator]"的方法绑定);
  3. 从前文可知,property_accessor正是通过"以[开头、以]结尾即按计算键原样拼接"这一规则来识别并透传 Symbol 键的。

七、测试验证:getter/setter 全组合矩阵

仓库提供了系统性的测试来验证 getter/setter 与js_name、显式属性名之间的全部组合,见 tests/wasm/getters_and_setters.rs 与配套的 tests/wasm/getters_and_setters.js。

Rust 侧定义了大量命名刻意区分的组合,例如:

  • 无js_name、无显式属性名(默认同名 getter、set_前缀 setter);
  • 仅显式属性名(getter = new_.../setter = new_...),函数名完全自由;
  • 仅js_name(重命名 Rust 侧方法名);
  • js_name+ 显式属性名同时使用;
  • getter 与 setter 使用相同属性名、不同 Rust 方法名的场景。

JS 侧测试脚本则逐一断言:默认模式下 Rust 方法以函数调用形式出现(rules.no_js_name__...()),而getter/setter声明的属性以直接属性读写的形式出现(rules.new_js_name__... = value * 2),从而端到端验证了编译产物的行为符合文档所述语义。

八、快速参考:关键规则一览

规则说明
组合前提必须与method一起使用,函数需带this: &T参数
getter 默认命名访问与 Rust 函数同名的 JS 属性
setter 默认命名Rust 函数名必须以set_开头,访问set_之后的部分
显式指定getter = prop/setter = prop,指定后函数名无任何限制
默认访问模式加载时沿构造函数原型链查找一次并缓存访问器
structural每次访问动态遍历原型链(当前已是默认语义,属性主要为向后兼容保留)
Symbol 键仅接受精确形式"[Symbol.<ident>]",同语法亦适用于js_name
实例 vs 静态实例访问器作用于实例对象;静态成员由编译器生成ClassGetter/ClassSetter分支

综上,getter/setter属性是 wasm-bindgen 在 Rust 侧表达 JavaScript 属性访问的标准方式:默认命名规则让常见场景零配置即可工作,显式属性名与js_name的组合提供了完整的命名自由,structural切换访问模式,而"[Symbol.<name>]"形式打通了 well-known symbol 的绑定通道。理解这三层能力(命名、访问模式、Symbol 键)后,你就能在 Rust 中准确、高效地操作任何 JavaScript 类实例的属性。

  • 开发工具

【免费下载链接】wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript

项目地址:https://gitcode.com/gh_mirrors/wa/wasm-bindgen
点击查看免费下载
上一篇:开源游戏美术资源库体验记:一条克隆命令,装满整个明日方舟素材包
下一篇:Burp Suite 汉化教程:3 分钟用免费开源方案让安全测试工具界面说中文

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 2:34:49

基于SpringBoot的高校实习管理系统(源码+lw+部署文档+讲解等)

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/10/6 2:33:47

【SI_I2S】快速掌握I2S信号总线测试方案

目录 1. I2S概述 2. I2S基本信号 2.1. I2S基本信号线 2.2. I2S协议中常见的参数 3. I2S主从工作模式 4. I2S数据传输模式 4.1. 飞利浦标准&#xff08;I2S&#xff09;模式 4.2. 左对齐&#xff08;Left Justified&#xff09;模式 4.3. 右对齐&#xff08;Right Ju…

作者头像 李华
网站建设 2026/10/6 2:30:51

VueUse useNProgress 实战:为 Vue 3 应用接入响应式顶部进度条

前端 【免费下载链接】vueuse Collection of essential Vue Composition Utilities for Vue 3 项目地址&#xff1a; https://gitcode.com/gh_mirrors/vu/vueuse 点击查看 免费下载 useNProgress 是 VueUse Integrations 系列中对 nprogress 的响应式封装&#xff0c;让你以 V…

作者头像 李华