ES新提案:双问号操作符

作者: Fundebug | 来源:发表于2019-08-28 12:54 被阅读0次

    摘要: 简单实用的新特性。

    本文主要讲Gabriel Isenberg撰写的ES提案“Nullish coalescing for JavaScript”。 它提出?? 替换||的运算符,并提供默认值。这里先把这相提案叫作双问号操作符,如果你有好的叫法,欢迎留言讨论。

    1. 概述

    双问号 ?? 的操作符跟 || 类似,如果给定变量值为 null 或者 undefined,刚使用双问号后的默认值,否则使用该变量值。

    如下:

        > undefined ?? 'default'
        'default'
        > null ?? 'default'
        'default'
        > false ?? 'default'
        false
        > '' ?? 'default'
        ''
        > 0 ?? 'default'
        0
    

    2. 早期的 || 运算符号

    直接来个例子来演示一下 || 运算,下面两个等式是等价的:

        a || b
        a ? a : b
    

    如果 a 是 truthy 值,则返回 a, 否则返回 b

    这使得使用||指定一个默认值成为可能,如果实际值是假的,那么将使用这个默认值:

        const result = actualValue || defaultValue;
        function getTitle(fileDesc) {
          return fileDesc.title || '(Untitled)';
        }
        const files = [
          {path: 'index.html', title: 'Home'},
          {path: 'tmp.html'},
        ];
        assert.deepEqual(
          files.map(f => getTitle(f)),
          ['Home', '(Untitled)']);
    

    请注意,基本只有在实际值undefined或为null时才应使用默认值,这是有效的,因为undefinednull都是假(虚值)的:

        > undefined || 'default'
        'default'
        > null || 'default'
        'default'
    

    遗憾的是,如果实际值是其他的虚值,也会使用默认值:

        > false || 'default'
        'default'
        > '' || 'default'
        'default'
        > 0 || 'default'
        'default'
    

    因此,这个getTitle()并不总能正常工作:

        assert.equal(
          getTitle({path: 'empty.html', title: ''}),
          '(Untitled)');
    

    3. 使用双问号操作符来解决 || 运算的问题

    ?? 主要是用来解决 || 操作符号的一些问题,以下两个表达式是等价的:

        a ?? b
        a !== undefined && a !== null ? a : b
    

    默认值是这样提供的:

        const result = actualValue ?? defaultValue;
    

    对于undefinednull??操作符的工作原理与||操作符相同

        > undefined ?? 'default'
        'default'
        > null ?? 'default'
        'default'
    

    除了 undefinednull的其它虚值,?? 不会返回默认值。

        > false ?? 'default'
        false
        > '' ?? 'default'
        ''
        > 0 ?? 'default'
        0
    

    使用 ?? 来重写 getTitle():

        function getTitle(fileDesc) {
          return fileDesc.title ?? '(Untitled)';
        }
    

    现在使用fileDesc调用它,它的.title是空字符串,仍然可以按符合咱们的预期工作:

        assert.equal(
          getTitle({path: 'empty.html', title: ''}),
          '');
    

    3.1 通过解构给定默认值

    除了使用 ??getTitle添加默认值,咱们也可以通过解构方式来给定默认值:

        function getTitle({title = '(Untitled)'}) {
          return title;
        }
    

    3.2 使用 ?? 操作符号的实际例子

    作为一个现实的例子,咱们使用??来简化下面的函数。

        function countMatches(regex, str) {
          if (!regex.global) {
            throw new Error('Regular expression must have flag /g: ' + regex);
          }
          const matchResult = str.match(regex); // null or Array
          if (matchResult === null) {
            return 0;
          } else {
            return matchResult.length;
          }
        }
        
        assert.equal(
          countMatches(/a/g, 'ababa'), 3);
        assert.equal(
          countMatches(/b/g, 'ababa'), 2);
        assert.equal(
          countMatches(/x/g, 'ababa'), 0);
        
        // Flag /g is missing
        assert.throws(
          () => countMatches(/a/, 'ababa'), Error);
    

    使用 ?? 操作符号后,简化如下:

        function countMatches(regex, str) {
          if (!regex.global) {
            throw new Error('Regular expression must have flag /g: ' + regex);
          }
          return (str.match(regex) ?? []).length;
        }
    

    3.3 双问号(??)操作符与可选链(?)

    双问号(??)的提出是为了补充可选链(?),来看看这两兄弟结合使用的场景(第A行):

        const persons = [
          {
            surname: 'Zoe',
            address: {
              street: {
                name: 'Sesame Street',
                number: '123',
              },
            },
          },
          {
            surname: 'Mariner',
          },
          {
            surname: 'Carmen',
            address: {
            },
          },
        ];
        
        const streetNames = persons.map(
          p => p.address?.street?.name ?? '(no name)'); // (A)
        assert.deepEqual(
          streetNames, ['Sesame Street', '(no name)', '(no name)']
        );
    

    4. 兼容性

    可以通过ECMAScript Next compatibility table 查看 ?? 支持情况。

    代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

    关于Fundebug

    Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了20亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

    相关文章

      网友评论

        本文标题:ES新提案:双问号操作符

        本文链接:https://www.haomeiwen.com/subject/raupectx.html