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/proposal-negated-in-instanceof.md.
  • 简体中文
  • Negated in and instanceof operators S1

    中文标题:取反的 in 和 instanceof 运算符

    提案概览
    提案速览

    该提案引入了取反运算符 !in!instanceof,以简化对 ininstanceof 检查的逻辑取反表达。它解决了在表达式中使用 ! 运算符时因错误加括号而产生的常见错误和可读性问题。

    Note

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

    取反的 ininstanceof 运算符

    状态

    阶段: 1
    负责人: Pablo Gorostiaga Belio (@gorosgobe)

    作者

    Pablo Gorostiaga Belio (@gorosgobe)

    提案

    演示文稿

    动机

    JavaScript 的 ininstanceof 运算符大致具有以下行为:

    a in obj; // 如果属性 a 存在于 obj 或其原型链中,则返回 true,否则返回 false
    a instanceof C; // 如果 C.prototype 存在于 a 的原型链中,则返回 true,否则返回 false

    为了取反这些表达式的结果,我们可以使用逻辑非(!)运算符将它们包裹起来:

    !(a in obj);
    !(a instanceof C);

    以这种方式取反 in/instanceof 表达式存在一些问题:

    易出错1

    逻辑非运算符 ! 必须应用于整个表达式才能产生预期的结果。对子表达式(可以是任意长度表达式的一部分)的错误加括号和/或将 ! 运算符应用于错误的操作数可能导致难以调试的错误,例如:

    对于 in

    if (!a in obj) { 
      // 将不会执行,除非 obj 具有 'true' 或 'false' 键
      // `in` 接受字符串或符号作为左侧参数,否则会将所有其他值强制转换为字符串
      // 参见 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion
    }
    
    // 正确用法
    if (!(a in obj)) {
      // ...
    }

    对于 instanceof

    if (!a instanceof C) { 
      // 将不会执行,除非 C 提供对布尔值返回 true 的 @@hasInstance 方法
    }
    
    // 正确用法
    if (!(a instanceof C)) {
      // ...
    }

    这种类型的错误相当常见。对于 in,此 Sourcegraph 查询 显示在 GitHub 上拥有数千星标的仓库中存在许多此类问题(我运行时有超过约 2100 个实例)。虽然存在一些误报(例如来自评论),但我在下面突出显示了一些值得注意的例子:

    示例
    仓库错误星标链接问题
    meteor/meteor!key in validDevices43.6k链接问题
    oven-sh/bun!"TZ" in process.env42.7k链接问题
    SergioBenitez/Rocket!"message" in msg || !"room" in msg || !"username" in MSG21.1k链接问题
    jeromeetienne/AR.js!'VRFrameData' in window15.7k链接问题
    duplicati/duplicati!'IsUnencryptedOrPassphraseStored' in this.Backup9.1k链接问题
    WebKit/WebKit!'openDatabase' in window6.4k链接问题
    buildbot/buildbot!option in options5.1k链接问题
    cloudflare/workerd!type in this.#recipes4.9k链接问题
    muicss/mui!'rows' in rest4.5k链接问题
    jlord/git-it-electron!'previous' in curCommit4.4k链接问题
    zlt2000/microservices-platform!'onhashchange' in W4.2k链接问题
    thechangelog/changelog.com!"execCommand" in document2.6k链接问题
    kiwibrowser/src!intervalName in this.intervals2.3k链接问题
    drawcall/Proton!'defineProperty' in Object2.3k链接问题
    montagejs/collections!index in this2.1k链接问题

    同样,对于 instanceof,此 Sourcegraph 查询 显示也存在许多此错误的实例(我运行时约有 1.9 万次)。如前所述,拥有数千星标的仓库也受到影响。以下是一些示例:

    示例
    仓库错误星标链接问题
    odoo/odoo!e instanceof o30.1k链接问题
    facebook/flow!flow instanceof RegExp22k链接问题
    v8/v8!e instanceof RangeError21.5k链接问题
    linlinjava/litemall!re instanceof RegExp18.2k链接问题
    iissnan/hexo-theme-next!elem instanceof Element15.8k链接问题
    chromium/chromium!this instanceof Test15.3k链接问题
    arangodb/arangodb!context instanceof WebGLRenderingContext13.1k链接问题
    ptmt/react-native-macos!response instanceof Map11.3k链接不适用(已弃用)
    chakra-core/ChakraCore!e instanceof TypeError8.9k链接问题
    icindy/wxParse!ext.regex instanceof RegExp7.7k链接问题
    WebKit/WebKit!e instanceof Error6.4k链接问题
    golden-layout/golden-layout!column instanceof lm.items.RowOrColumn6k链接问题
    janhuenermann/neurojs!config instanceof network.Configuration4.4k链接问题
    gkz/LiveScript!last instanceof While2.3k链接问题
    CloudBoost/cloudboost!obj instanceof CB.CloudObject || !obj instanceof CB.CloudFile || !obj instanceof CB.CloudGeoPoint || !obj instanceof CB.CloudTable || !obj instanceof CB.Column1.4k链接问题

    在彭博社内部,我们鼓励使用 eslint 和 TypeScript,它们各有针对这些情况的错误提示。然而,由于我们允许团队自行决定部分工具选择,错误仍然蔓延开来:在一大批内部项目中,我们发现大约八分之一的 in/instanceof 用法是取反的 ininstanceof 表达式。超过 1% 的取反 in 用法存在此错误。这也影响了取反的 instanceof,其中超过 6% 的用法存在此错误。我们的内部结果与外部 sourcegraph 查询的数据一致:取反的 instanceof 表达式中的错误发生率明显高于取反的 in 表达式。虽然我们现在正在内部修复此问题,但总体而言,这些结果表明,由于取反的 ininstanceof 表达式缺乏易用性,这是一个常见问题。

    产生困惑

    这些表达式的取反与具有取反版本的运算符(如 ===/!==)不一致。这会在开发人员中产生困惑,并导致诸如 JavaScript 中是否有用于检查对象属性的“not in”运算符?Javascript !instanceof If 语句 等高赞高浏览问题。

    可读性

    为了取反 in/instanceof 表达式的结果,我们需要引入额外的分组运算符(用两个括号表示)。此外,not 位于表达式的开头,这与自然英语的读法不同。这两个因素共同导致代码可读性降低。

    较差的开发体验

    在条件语句中使用 in/instanceof 作为守卫是很常见的。反转这些条件以减少代码缩进(如这与代码复杂度相关所示)可以提高代码的可读性和质量。使用现有运算符时,反转条件中的表达式需要同时用括号包裹并且取反。

    解决方案

    !in,是 in 的取反版本,其中

    a !in obj;

    等价于

    !(a in obj);

    !instanceof,是 instanceof 的取反版本,其中

    a !instanceof obj;

    等价于

    !(a instanceof obj);
    • 更安全:不再需要引入额外的分组,取反直接应用于运算符本身,而不是应用于表达式中左侧操作数旁边。
    • 提高可读性:不再需要额外的分组来取反表达式的结果。这与 !== 等其他运算符保持一致。读起来更自然,更直观。
    • 更好的开发体验:同样,在重构代码时更容易更改 - 只需添加一个 ! 即可取反表达式。

    在其他语言中:

    Python:

    if item not in items:
      pass
    
    if ref1 is not ref2:
      pass

    Kotlin:

    if (a !in arr) {}
    
    if (a !is SomeClass) {}

    C#:

    if (a is not null) {}

    Elixir:

    a not in [1, 2, 3]

    相关提案

    模式匹配

    模式匹配提案提出了一种新的关系表达式,如 a in ba instanceof b,使用新的运算符 ishttps://github.com/tc39/proposal-pattern-matching#is-expression

    ininstanceof 类似,我们可以扩展该提案以包含取反的 is 运算符,例如 !is

    Footnotes

    1. 关于 TypeScript 的说明:如果使用 TypeScript,易出错性没有那么令人担忧,因为 TypeScript 会检查传递给 ininstanceof 运算符的类型是否正确。但是,类型错误或缺少类型仍然可能导致此问题。如果你不使用 TypeScript,或者代码的特定部分是无类型的或显式使用 any,也可能发生这种情况。