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/stage/1/proposal-class-brand-check.md.
  • 简体中文
  • Class Brand Checks S1

    中文标题:类品牌检查

    提案概览
    提案速览

    该提案为 ECMAScript 类引入了一种 class.hasInstance() 语法形式,提供直接的品牌检查,以测试对象是否具有类品牌,这比私有字段的“鸭子类型”检查更为通用和直观。它解决了 instanceof(基于原型)的局限性,并避免为品牌检查添加不必要的私有字段。

    Note

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

    class.hasInstance()

    本提案为 ECMAScript 代码类添加了一种 class.hasInstance() 品牌检查 语法形式。

    class C {
      static isC(o) {
        return class.hasInstance(o)
      }
    }
    let c = new C()
    let o = Object.create(c)
    let p = new Proxy(c, {})
    C.isC(c) // true
    C.isC(o) // false
    C.isC(p) // false

    阶段:1

    作者:HE Shi-Jun (@hax), XU Tian-Yang (@XGHeaven), Tu Qiang(@YuriTu)

    提案 champion:HE Shi-Jun, XU Tian-Yang, Tu Qiang

    实验性实现

    动机

    关于品牌检查有着很长的讨论历史,并且在该领域有多种努力。最近的相关提案是“私有字段的品牌检查”。在讨论该提案时,https://github.com/tc39/proposal-private-fields-in-in/issues/13 的作者提出了另一种设计方向。与其采用鸭子类型风格的检查(#field in obj),我们可以提供一种更通用的类品牌检查(class.hasInstance(obj)),即一种“真正的”instanceof 检查。在许多情况下,这更接近程序员的意图,并且与其他基于类的面向对象编程语言的概念模型更相似。

    class C {
      static isC(o) {
        return o instanceof C
      }
    }

    instanceof 默认是基于原型的,因此 C.isC(Object.create(C.prototype)) 将通过测试。请注意,许多程序员仍然使用 instanceof,尤其是那些来自其他面向对象语言的程序员。即使他们知道 instanceof 的局限性,并且存在 Symbol.hasInstance 可用于“定制”instanceof,人们也会忽略它们,因为没有简单的方法来“修复”它。

    class C {
      #x
      static isC(o) {
        return #x in o
      }
    }

    正如 private-in 提案所述,人们可以利用私有字段和私有 in 语法来执行品牌检查,但这只是对私有字段的过度使用。即使人们实际上不需要私有字段,也需要添加一个私有字段。不幸的是,私有字段与普通数据属性在语义上存在显著差异,因此引入私有字段可能会引入破坏性变更,并可能降低代码与许多库/框架的互操作性。

    即使你已经出于正当理由使用私有字段,这也会导致问题:

    class C {
      #x
      #y // 添加一个新字段
      static isC(o) {
        return #x in o // 我们是否需要将其更新为
        // return #x in o && #y in o
      }
    }

    程序员是否应该更新 isC 的实现?如果你将 #x in o 视为类似鸭子类型检查的事物,你应该添加 && #y in o,但在几乎所有情况下,这段代码是冗余的。但是,在某些边缘情况下(例如 #y 的初始化器抛出,如当前私有字段草案所规定,你会得到一个具有 #x 但没有 #y 的对象),你需要再次考虑。这给编码者和代码审查者带来了困惑和不必要的负担。

    根本问题是,在大多数情况下,人们想要的是一个通用的“instanceof”测试,逐个测试字段的存在性并不能真正传达程序员的意图。

    一个更好的解决方案:

    class C {
      static isC(o) {
        return class.hasInstance(o) // class.hasInstance 是一个元方法,用于检查 o 是否具有 C 的类品牌
      }
    }

    实际上,它也允许程序员使用 Symbol.hasInstance 来“修复”instanceof,如果他们愿意的话:

    class C {
      static [Symbol.hasInstance](o) {
        return class.hasInstance(o) // class.hasInstance 是一个元方法,用于检查 o 是否具有 C 的类品牌
      }
    }

    演示文稿

    类品牌检查与私有字段交互的边缘情况分析

    待定

    语法和命名问题

    class.hasInstance 元语法与 类属性访问表达式 提案冲突,已经有一个 issue 讨论此事。如果我们决定将 class.foo 保留给类属性,我们需要找到其他语法,例如 class::hasInstance

    关于命名,我选择了 .hasInstance,它遵循 Symbol.hasInstance,尽管总会有其他选择:.hasBrand.checkBrand