For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/year/pending/proposal-composable-accessors.md.
  • 简体中文
  • Composable Accessors via built-in decorators S1

    中文标题:通过内置装饰器实现可组合访问器

    提案概览
    提案速览

    该提案为可组合访问器引入了内置装饰器,解决了诸如惰性求值、验证、规范化和副作用等常见访问器模式。它利用现有的自动访问器和别名访问器提案作为基线,并在其上叠加额外行为。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    通过内置装饰器实现可组合访问器

    状态

    阶段:1(截至 2026 年 1 月全体会议)

    作者/提案负责人:Lea Verou (@leaverou)

    历史记录,请参见 原始提案

    目录

    1. 状态
    2. 动机
      1. 为什么使用内置装饰器而不是语法?
      2. 为什么不只依赖用户态装饰器?
    3. 示例
    4. 当前想法索引

    动机

    有一大类访问器用例具有很强的共性,它们理应获得更好的开发者体验和工具支持。

    这些访问器大多是加性的_或_可组合的: 不同于常规访问器所围绕的“用任意代码替换属性”的心智模型, 可组合访问器是基于值的:作为基线,它们代理另一个属性(默认存储内部槽中,或由另一个属性提供),任何验证逻辑、转换、副作用等都叠加在该基线之上

    底层的基于值的管道由 自动访问器别名访问器 提供。 然后,通过内置装饰器将额外功能叠加在该基线上,这正是本提案的主题。

    一些例子包括:

    • 惰性求值: 将昂贵的计算延迟到访问属性时,然后缓存它们
    • 数据验证: 拒绝某些写入(大声或静默)
    • 数据规范化: 接受多种格式,但仅存储规范化版本
    • 副作用: 在写入之前或之后运行代码

    本提案旨在探索这些用例中哪些可能具有较好的影响/工作量比,以作为内置 装饰器 暴露。

    为什么使用内置装饰器而不是语法?

    关于可组合访问器的 原始提案 定义了诸如 validatetransform 等描述符键。 然而,装饰器相比语法有几个优势:

    • 它们是一个小得多的增量,提高了影响/工作量比
    • 运行时语义可以被 polyfill,而语法需要转译
    • 可以在有意义的情况下扩展到非属性值。例如,有潜在的扩展来支持 函数参数let 变量const 变量 上的装饰器。这些装饰器中的大多数在那里也可能极其有用。

    使用装饰器确实带来一些缺点,但它们都可以被缓解:

    问题描述缓解措施
    可读性传递大参数时可读性差,将可能多行的辅助信息放在核心信息(属性名和初始值)之前。对于较长的参数使用引用。
    有损性装饰器通过包装来修改访问器函数,这会抹掉原始的 setter。装饰器实现可以被设计为在属性上保留原始 setter。甚至可能为 Function 提供一个通用的特性来实现这一点。
    命令式 API装饰器不能以命令式方式应用。许多用例可以通过存根装饰器函数并将结果应用到属性描述符来缓解。

    为什么不只依赖用户态装饰器?

    • 因为这些用例足够普遍,应该开箱即用
    • 因为工具无法从用户态装饰器中提取含义
    • 因为内置装饰器保证适用于所有相关种类

    示例

    本提案旨在与 分组和自动访问器提案别名访问器提案 结合使用,以改善人体工程学。

    import isNumeric from "./validators.js";
    const { validate, lazy } = Decorators;
    
    class C {
    	@validate(isNumeric)
    	accessor min = 0;
    
    	@validate(function(v) { return v >= this.min; })
    	accessor max = 100;
    
    	@memoized get foo () {
    		return expensiveComputation();
    	}
    }

    除了各个装饰器的细节外,另一个问题是这些装饰器的命名空间应该是什么? 上面的例子使用了一个新的 Decorators 全局对象。也许它可以是一个子对象,例如 Decorators.known?或者完全不同?

    注意,因为装饰器只是变量,它们也可以被别名为不同的名称。 例如:

    import { array } from "./transformers.js";
    const { normalize: to } = Decorators;
    
    class C {
    	@to(array) accessor options;
    }

    当前想法索引

    IMPORTANT

    这是对哪些类型的内置装饰器可能有用进行的早期探索。 因此,它倾向于广度,但预期会随着时间的推移而缩小范围并完善。 示例代码仅为说明性质,不旨在成为生产就绪或经过全面测试。