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-deprecated.md.
  • 简体中文
  • deprecated ?

    中文标题:弃用标记

    提案概览
    提案速览

    该提案引入 deprecated; 全局变量或 'deprecated'; 指令,以标准化标记弃用代码,帮助工具和虚拟机进行识别和处理。它提供了灵活的选择,如自动去优化或抛出标志,并讨论了使用 Symbol.deprecated 的 polyfill 策略。提案处于早期阶段,尚无正式规范或委员会决定,正在探索装饰器和内在函数等替代方案。

    Note

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

    弃用的内容

    动机

    先例

    提案

    该提案旨在向语言引入一个新的 deprecated; 全局变量,或一个新的 'deprecated'; 指令(pragma),以便通过工具和虚拟机实现,用一种标准的方式轻松识别弃用的代码。

    例如:

    function deprecatedFunction() {
      deprecated;
      // 做事情
    }
    
    // 或者 
    
    function deprecatedFunction() {
      'deprecated';
      // 做事情
    }

    工具支持:

    • 支持 deprecated 机制的虚拟机实现可以提供在遇到 deprecated 代码时抛出的选项(类似于 node --throw-deprecation 的想法),或者提供一个嵌入器 API,在编译 deprecated 代码时接收回调,允许 Node.js 等嵌入器能够自定义处理。(类似于 Node.js 的 process.on('warning', (warning) => { /* ... */ }) API 的想法)
    • 虚拟机实现可以在任何弃用作用域中自动对代码进行去优化,并在必要时提供忽略 deprecated 语句的选项。关键在于,使用弃用代码应该有默认的惩罚,用户需要明确选择退出。(想法类似于:node --no-deprecation
    • 调试器、IDE、linter 及相关工具可以提供关于弃用代码使用的可见警告。

    指令选项:'deprecated';

    或者,deprecated; 可以是一种指令类型的指令:

    function deprecatedFunction() {
      'deprecated';
      // 做事情
    }

    为了支持伴随代码的“弃用消息”的场景,我们可以使用如下形式:

    function deprecatedFunction() {
      'deprecated; This is deprecated, use something else';
      // 做事情
    }

    指令选项具有最少的边界情况,最容易进行 polyfill,并避免可能破坏用户代码的潜在冲突。

    同样值得注意的是,指令方法实际上不需要 TC-39 委员会的采取任何行动。VM 和工具提供商可以约定选择支持 'deprecated'; 指令。

    Polyfills 和处理歧义

    为了使其易于 polyfill 并避免破坏现有代码,这里的 deprecated 实际上可以是一个具有非常特定值的全局属性,而不是一个语言关键字。例如,可以默认定义一个新著名的标准符号 Symbol.deprecated,并将 global.deprecated = Symbol.deprecated。然后,当 VM 或工具在代码中遇到 deprecated;,并且 deprecated 等于 Symbol.deprecated 时,就按上述方式处理。

    在较旧的 VM 上,当 deprecated; 被 polyfill 而具有特殊含义时,该语句将是无操作。

    VM 在确定是否在运行时进行去优化时应该没有困难,但工具可能会看到一些歧义。例如:

    function foo(deprecated) {
      deprecated;  // 仅在 deprecated === Symbol.deprecated 时触发 VM 中的弃用语义
                   // 在工具中引起歧义,因为它无法可靠地确定 deprecated === Symbol.deprecated
    }

    注意:这里预期的 VM 行为是默认情况下自动对代码去优化,并且如果启用了某个标志,则可能抛出。

    在这种情况下,工具能做的最好的事情是显示关于歧义使用的警告,并提供一种机制(例如 lint 忽略规则)来退出对该特定情况的检查。

    另一个歧义的情况是,如果用户将 Symbol.deprecated 分配给另一个变量会怎样。它会有同样的效果吗?答案是否定的。明确的要求是表达式恰好是 deprecated;(带或不带 ; 是另一个完全不同的论点)。

    function foo() {
      const m = deprecated; // 不会触发 'deprecated' 语义
      m; // 不会触发 'deprecated' 语义
      
      deprecated; // 确实触发 'deprecated' 语义
      deprecated  // 确实触发 'deprecated' 语义
    }
    
    function foo() {
      deprecated = 'something else';
      deprecated; // 不会触发 'deprecated' 语义
    }

    注意:使用指令选项完全避免这些问题。

    我们可以采用类似 import { deprecated } from 'something' 的方法来引入内在,而不是将其设为全局。

    具体来说,

    import { deprecated as dep } from 'something'
    
    function foo() {
      dep;
     }

    这在 VM 行为方面是可以的,但在工具方面会使事情变得更加困难,特别是因为工具必须执行额外的更复杂的分析才能知道 dep; 实际上是什么意思。

    即使使用这样的机制,像 linter 这样的工具可能也一直难以可靠地检测到弃用代码的使用。例如,

    class Foo {
      bar() {
        deprecated
      }
    }
    
    require('some-module-that-monkey-patches-Foo')
    
    var foo = new Foo()
    foo.bar()

    在这种情况下,工具必须能够可靠地确定 bar() 是否已被 monkey-patched,才能给出任何合理的警告。这就是为什么严格依赖 linter 等工具是不够的。解决这种特定歧义没有银弹。

    替代方案:使用装饰器

    Decorators 提案提供了解决这个问题的潜在替代方法...

    @deprecated
    function deprecatedFunction() {}

    然而,这种方法带有一些重要的局限性:

    1. 语法不支持向后兼容或不容易进行 polyfill。具体来说,代码需要被转译以移除装饰器才能在旧版本的 Node.js 和旧浏览器上运行,而新的全局和指令方法可以轻松进行 polyfill。

    2. 在 Node.js 中,我们经常需要在函数内部标记弃用,例如,在某些情况下,只有函数的某些参数组合已被弃用。那么,要让装饰器方法工作,我们需要能够装饰单独的块,例如,

    function foo(...args) {
      if (typeof args[0] === 'string') {
        // 这是可以的
      } else
      @deprecated {
        // 这是已弃用的  
      }
    }

    替代方案:deprecated() 内在函数

    在 Node.js 中,我们不仅将代码标记为弃用,我们还为弃用关联一个静态标识符和消息,以向用户提供有用的附加信息。虽然这已被证明结果参差不齐,但我们可以在保持上述语义的同时遵循相同的基本模式。

    function deprecatedFunction() {
      deprecated('Use something else');
      // 做一些事情
    }

    默认的 VM 行为将相同:遇到时自动去优化,如果使用了某个标志则可以选择抛出。提供给函数的消息将提供错误消息文本。

    这种方法与提议的方法并不互斥,我们不是定义 global.deprecated === Symbol.deprecated(),而是让 global.deprecated === [内在函数 deprecated()],并具有相同的基本语义。

    然而,这可能遇到问题的地方是在前面描述的歧义情况下,用户代码将 deprecated 分配给其他值:

    function foo(deprecated) {
      deprecated('Use something else') // 如果 deprecated 恰好不是函数,则失败
    }