最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • [译] Optional-Chaining 操作符

    正文概述 掘金(ICBU互动及端技术团队)   2021-02-08   521

    作者:ICBU 东墨

    optional-chaining 是一个 Javascript 中可以增强惰性求值时健壮性的特性,2019 年 3 月,v8 实现了该特性, 此后一段时间,在 chrome 中作为一项实验性 flag 被打开。在本文中, 如无特别说明, 所有的代码均在 typescript 运行. 在 typescript 3.7 之前, ts 并未原生支持 optional-chaining 操作符; 在 2019 年 11 月发布的 typescript 3.7 中, ts 已支持 optional-chaining 操作符为一级特性.

    快速处理一个数状数据结构

    我们往往会在处理数据时遇到需要层层读取树状数据的情况,往往是为了取得某个层级的数据. 考虑下面这样来自于后端 API 的数据, 我们需要从一条 user 记录中, 取得其首 primary office 的 city name:

    const apiResult = {
      name: "Neville Bowers",
      office: {
        primary: {
          city: "San Francisco",
          state: "CA"
        }
      }
    }
    

    为了得到想要的数据,我们会这样写:

    const city = apiResult.office.primary.city;
    // --> "San Francisco"
    

    简单明了. 然而, 这段代码成立的潜在前提是, API 返回总是会在运行时返回稳定的结构. 但在实际中, 我们会遇到 API 返回的结果中, user 结构中缺少 office 字段的情况:

    const apiResult = {
      name: "Yatharth Agarwal"
    }
    const city = apiResult.office.primary.city;
    // --> Uncaught TypeError: Cannot read property 'primary' of null
    

    这就有问题. 进一步地说,如果我们没有想到要去 catch 异常,我们的应用可能就会崩溃!显然我们需要在层层读取 apiResult 的时候更加小心 —— 往往事后想起来我们觉得理所应当,但是开发时很可能忽略的,尤其是当我们的 API 在大多数时候返回稳固的数据结构时,或者是相关的 API 文档未给出返回数据的所有可能状态。

    对于这种情况,我们最保守的处理手段是假设在这种树状结构中,每个层级的所有字段都是可能不存在的. 让我们来探索一下有什么具体的手段来处理可能缺失的字段.

    a/ 层层判定是否存在的逻辑

    我们可以层层判定某个字段是否存在,以此确定取值逻辑。

    let city: string | undefined = undefined;
    if (apiResult) {
      const office = apiResult.office;
      if (office) {
        const primary = office.primary;
        if (primary) {
          city = primary.city;
        }
      }
    }
    

    这当然是个有效的方法, 可以对运行时中不可预期的 apiResult 结构做弹性处理, 但这段代码太冗长难读. 一些开发者会把这种代码叫做"死亡金字塔". 另外, 这种写法需要我们用 let 使得 city 可被重新赋值,就代码规范而言这是一种开倒车。

    b/ 层层递进的三元表达式

    我们可以使用层层递进的三元表示来安全地获取 city 字段:

    const city = !apiResult
      ? undefined
      : !apiResult.office
        ? undefined
        : !apiResult.office.primary
          ? undefined
          : apiResult.office.primary.city;
    

    这种方法比上一种方法简洁些(译者注:哪里简洁了……),并且允许我们保持 city 的不可变性(使用了 const); 但是这段代码依然是难读且有冗余的(比如, undefined 出现了 3 次).

    c/ 逻辑表达式

    我们也可以使用逻辑表达式来安全地取得 city 字段:

    const city =
      apiResult &&
      apiResult.office &&
      apiResult.office.primary &&
      apiResult.office.primary.city;
    

    这段代码比层层递进的三元表达式又简洁了一些(译者注: 并没有……), 然而它依然很冗长.

    d/ try/catch 块

    我们还可以使用 try/catch 块来安全地取得 city 字段:

    let city: string | undefined = undefined;
    try {
      city = apiResult.office.primary.city;
    } catch (error) {
      // Swallow Error
    }
    

    try/catch 这种途径最接近"直接读取". 但如果为了取值就这样写的话, 就会在代码里写出大量的 try/catch 块, 而且这里的 city 变量也得是可被重新赋值的变量.

    e/ lodash, ramda 等

    一些库提供了安全的进行树状结构梳理的方法. 比如, 在 lodash 中, 我们可以使用 get 方法来安全地得到 city:

    import * as _ from 'lodash';
    const city = _.get(apiResult, 'office.primary.city', undefined);
    

    这种方案是到目前为最简洁的方法, 代码长度只比"直接读取"长了一点点. 然而, 为此我们付出了开发时的代价:

    • 在层层取值时候的 IDE 提示.
    • 对取值路径 office.primary.city 的编译器类型推导(即 city 的类型是不确定的, 除非你明确指定)

    Optional-Chaining 操作符

    上文提到每种对树状数据层层取值方案都有其短板. 那么有没有一种更完美的方案?

    有, 就是 Optional-Chaining 操作符

    //  if `a` is `undefined` or `null`:
    //    return `undefined`
    //  else: 
    //    return `a.b`
    a?.b;
    // The optional chaining operator is equivalent to:
    (a == null) ? undefined : a.b;
    

    ( 以上引用自 github.com/tc39/propos… )

    在别的语言中, 这种操作符往往被命名为为 safe navigation 操作符, existential 操作符, null-conditional 操作符, 或者 null propagation operator.

    Optional Chaining 操作符可以组合起来使用, 以优雅地处理需要从树状数据层层取值的场景: 在连续使用的 Optional Chaining 操作符中, 如果中间有任何地方返回 null 或者 undefined, 整个联调会返回 undefined.

    回到我们的例子, 如果使用 Optional Chaining 操作符, 写法会是这样:

    const city = apiResult?.office?.primary?.city;
    // --> string | undefined
    

    你看, 这种语句对我们的问题就是一个非常直接的解决方案. 它很简洁, 可读性高, 且保留了所有的开发时工具提示.

    在 TypeScript 在 3.7 之前不支持这个特性, 在那之前, TypeScript 在等待TC39 确认是否要将对 Optional-Chaining 操作符的支持列入标准. 如果你还在使用 TypeScript 3.7 之前的版本, 可以通过 ts-optchain 这个包来使用 optional-chaining.


    起源地下载网 » [译] Optional-Chaining 操作符

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元