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/4/proposal-logical-assignment.md.
  • 简体中文
  • Logical Assignment Operators S4

    中文标题:逻辑赋值运算符

    提案概览
    提案速览

    该提案引入了逻辑赋值运算符(||=、&&=、?=),将逻辑运算符与赋值表达式相结合,允许简洁的默认值设置和更新,同时利用短路语义避免不必要的 setter 调用。

    Note

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

    proposal-logical-assignment

    一个将逻辑运算符与赋值表达式结合起来的提案:

    // "或或等于"(或者,木槌运算符 :wink:)
    a ||= b;
    a || (a = b);
    
    // "与与等于"
    a &&= b;
    a && (a = b);
    
    // "QQ等于"
    a ??= b;
    a ?? (a = b);

    状态

    当前阶段:4

    倡导者

    动机

    便捷运算符,灵感来源于Ruby的。我们已经有十多个数学赋值运算符,但没有针对常用的逻辑运算符的赋值运算符。

    function example(a) {
      // 默认将 `a` 设为 "foo"
      if (!a) {
        a = 'foo';
      }
    }

    if 语句可以工作,但简洁性会更好。尤其是在处理深层属性赋值时:

    function example(opts) {
      // 可以,但可能触发 setter。
      opts.foo = opts.foo ?? 'bar'
    
      // 不会触发 setter,但写起来"感觉不对"。
      opts.baz ?? (opts.baz = 'qux');
    }
    
    example({ foo: 'foo' })

    有了这个提案,我们可以获得简洁性,而且不必忍受不必要的 setter 调用:

    function example(opts) {
      // 不会不必要地调用 setter。
      opts.foo ??= 'bar'
    
      // 不需要重复写 `opts.baz`。
      opts.baz ??= 'qux';
    }
    
    example({ foo: 'foo' })

    语义

    逻辑赋值运算符的工作方式与其数学赋值友略有不同。虽然数学赋值运算符_总是_触发设置操作,逻辑赋值利用其短路语义在可能时避免设置操作。

    let x = 0;
    const obj = {
      get x() {
        return x;
      },
      
      set x(value) {
        console.log('setter called');
        x = value;
      }
    };
    
    // 这总是会记录 "setter called"
    obj.x += 1;
    assert.equal(obj.x, 1);
    
    // 逻辑运算符不会不必要地调用 setter
    // 这不会记录日志。
    obj.x ||= 2;
    assert.equal(obj.x, 1);
    
    // 但如果运算符没有短路,则会调用 setter
    // "setter called"
    obj.x &&= 3;
    assert.equal(obj.x, 3);

    在大多数情况下,设置操作被短路这一事实除了性能之外没有可观察的影响。但当它有副作用时,在适当的时候避免它通常是可取的。在下面的示例中,如果 .innerHTML setter 被无用触发,可能会导致丢失未序列化到 HTML 中的状态(如焦点):

    document.getElementById('previewZone').innerHTML ||= '<i>Nothing to preview</i>';

    参见 #3 中关于短路语义的讨论。它还指出了在像 obj.deep[key++] += 1obj.deep[key] = obj.deep[key++] + 1 这样的代码中,数学赋值运算符已经存在的差异。

    相关