【问题标题】:ES2020 optional chaining: what's the difference between a?.().b and a()?.b and a?.()?.bES2020 可选链:a?.().b 和 a()?.b 和 a?.()?.b 有什么区别
【发布时间】:2020-10-09 01:43:57
【问题描述】:

假设我们有这个对象:

let obj = {};

这些表达式的具体作用是什么?

  • obj.a?.().b
  • obj.a()?.b
  • obj.a?.()?.b

【问题讨论】:

  • 这只会给你未定义的。您有针对此特定场景定义的值的适当示例吗?
  • @Claies 令人惊讶的是,a?.() 不是语法错误。
  • @HereticMonkey 我已经阅读了文档。我仍然不明白它们之间的区别。
  • @MonsterCat 光看是没用的,多应用几次,你就会掌握窍门

标签: javascript ecmascript-next


【解决方案1】:

obj.a?.().b - 如果obj.anullundefined,则表达式为undefined,否则表达式的计算结果为obj.a().b

obj.a()?.b - 如果obj.a()nullundefined,则表达式为undefined,否则表达式的计算结果为obj.a().b

如果obj.aobj.a()nullundefined,则obj.a?.()?.b 则表达式为undefined,否则表达式的计算结果为obj.a().b

阅读更多关于可选链的信息here

【讨论】:

  • 为什么obj.a()?.b 会抛出错误?它不应该评估为undefined吗?
  • @MonsterCat obj.a 不是函数。 a 未定义。
【解决方案2】:

obj.a?.().b

  1. 直接取消引用obj

  2. 以空安全的方式取消引用obj.a - 如果属性不存在,它将在此时停止,它是undefinednull。如果发生这种情况,评估表达式的结果将是 undefined

  3. 直接执行该值。

  4. 接收返回结果并继续。

  5. 直接从结果中获取属性b

const tryIt = obj => {
  console.log("------start------");

  console.log("trying with", obj );

  try {
    console.log( "result", obj.a?.().b );
  } catch (e) {
    console.error("problem", e.message);
  }

  console.log("-------end-------");
}

tryIt( null );                 // ERROR
tryIt( {} );                   // undefined
tryIt( { a: undefined } );     // undefined
tryIt( { a: null } );          // undefined
tryIt( { a: false } );         // ERROR
tryIt( { a: "hello" } );       // ERROR
tryIt( { a: function() {} } ); // ERROR
tryIt( {                       // ERROR
  a: function() {
    return null; 
  }
});
tryIt( {                       // 42
  a: function() {
    return { b: 42 };
  }
});

obj.a()?.b

  1. 直接取消引用obj

  2. 直接取消引用obj.a

  3. 直接执行该值。

  4. 以 null 安全的方式处理该值 - 如果返回值为 nullundefined,它将在此时停止。如果发生这种情况,评估表达式的结果将是 undefined

  5. 直接从结果中获取属性b

const tryIt = obj => {
  console.log("------start------");

  console.log("trying with", obj );

  try {
    console.log( "result", obj.a()?.b );
  } catch (e) {
    console.error("problem", e.message);
  }

  console.log("-------end-------");
}


tryIt( null );                 // ERROR
tryIt( {} );                   // ERROR
tryIt( { a: undefined } );     // ERROR
tryIt( { a: null } );          // ERROR
tryIt( { a: false } );         // ERROR
tryIt( { a: "hello" } );       // ERROR
tryIt( { a: function() {} } ); // undefined
tryIt( {                       // undefined
  a: function() {
    return null; 
  }
});
tryIt( {                       // 42
  a: function() {
    return { b: 42 };
  }
});

obj.a?.()?.b

  1. 直接取消引用obj

  2. 以空安全的方式取消引用obj.a - 如果属性不存在,它将在此时停止,它是undefinednull。如果发生这种情况,评估表达式的结果将是 undefined

  3. 直接执行该值。

  4. 以 null 安全的方式处理该值 - 如果返回值为 nullundefined,它将在此时停止。如果发生这种情况,评估表达式的结果将是 undefined

  5. 直接从结果中获取属性b

const tryIt = obj => {
  console.log("------start------");

  console.log("trying with", obj );

  try {
    console.log( "result", obj.a?.()?.b );
  } catch (e) {
    console.error("problem", e.message);
  }

  console.log("-------end-------");
}


tryIt( null );                 // ERROR
tryIt( {} );                   // undefined
tryIt( { a: undefined } );     // undefined
tryIt( { a: null } );          // undefined
tryIt( { a: false } );         // ERROR
tryIt( { a: "hello" } );       // ERROR
tryIt( { a: function() {} } ); // undefined
tryIt( {                       // undefined
  a: function() {
    return null; 
  }
});
tryIt( {                       // 42
  a: function() {
    return { b: 42 };
  }
});

【讨论】:

    【解决方案3】:

    optional chaining operator 可以在三个位置使用:

    1. 代替点运算符进行属性访问 (obj?.a)
    2. 紧接在用于属性访问的括号语法之前 (obj?.['a'])
    3. 函数调用之前 (obj.a?.())

    在属性访问位置,如果属性值为空(即nullundefined),则立即将整个表达式短路,并返回undefined

    在函数调用位置,如果函数为空,则立即短路整个表达式,并返回undefined(从而避免“未定义不是函数”异常) .

    所以:

    • 如果obj.a 为空,obj.a?.().b 将返回undefined

    • 如果obj.a().b 的结果为空,obj.a()?.b 将返回undefined

    • 如果a 为空,或者obj.a() 的结果为空,obj.a?.()?.b 将返回undefined

    【讨论】:

      【解决方案4】:

      TL;TR

      使用? 检查表达式中的non-undefinednot-null 值,如果发现某个值是其中之一,则链会中断并返回undefined

      需要注意的重要一点是它 防范undefinednull 值而不是falsy 值,即它仍然会传递其他虚假值,例如NaN 或 ""。

      详细说明

      让我们从引用MDN开始

      可选的链接运算符提供了一种简化访问的方法 当引用可能时,通过连接的对象获取值 或函数可能未定义或为空。

      考虑这样一个对象:

       var obj = {
             a: {
                 b: 1
             },
           }
      

      现在要验证objobj 的属性a,然后验证a 的属性b 不是undefinednull,您可能需要执行以下操作:

       obj && obj.a && obj.a.b
      

      可选链接为您提供了一个替代方案。你可以这样做:

      obj?.a?.b
      

      现在,假设a 恰好是一个函数,它返回一个具有属性b 的对象,该对象包含一个值1。像这样的:

       var obj = {
                 a: function{
                     return {b:1}
                  },
             }
      

      那么,对于深度嵌套的 b,您现在将如何验证 obj 及其字段?好吧,你可以这样做:

      obj && obj.a && typeof obj.a === 'function' && obj.a() && obj.a().b
      

      或者你可以简单地做

      obj?.a()?.b
      

      这只是意味着检查obj是否为非未定义和非空,(如果是)然后检查obj?.a()是否相同,(如果是那么)执行obj的方法a()。如果执行后该方法返回一个值(不是undefinednull),则从中获取属性b 的值。

      在此检查期间的任何时候,如果发现一个值是undefinednull,只需断开链并返回undefined

      但是,如果a 不是在前面的表达式中使用typeof obj.a === 'function' 检查过的函数,这将失败。 另外,请注意,此时如果obj.a() 碰巧返回,比如“baz”,它将被执行为 "baz".b 会给你undefined

      有了这些知识,我们可以很容易地破译这个表达式:

         obj.a?.().b
      

      它检查obj.a 是否为non-undefinednot-null。如果是,则执行 obj.a()(请注意,如果 obj.a 不是函数而是返回数字 5,则会抛出错误)

      如果obj.a() 发生成功执行,则将检查返回值的.b

      现在这是剩下的

      obj.a?.()?.b
      

      嗯,这也像我们之前的执行一样执行:

      检查obj.a 是否为not-nullnon-undefined。如果是,请执行obj.a。如果那也是non-nullnon-undefined,执行obj.a()等等。

      如果在检查时的任何时候,发现值是nullundefined,链就会中断。

      非常重要的一点要注意,我们的操作符 ? 只防范 undefined 和 null 而不是防范其他 falsy 值。

      考虑一下

      var a = undefined
      var myVal = a?.details.b;
      alert(a) //will return undefined
      
      var a = ""
      var myVal = a?.details.b;
      alert(a) //Our guard fails us here and simply throws an error

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-27
        • 2012-07-03
        • 1970-01-01
        • 1970-01-01
        • 2013-06-25
        • 2017-04-06
        • 1970-01-01
        • 2010-10-11
        相关资源
        最近更新 更多