为什么我喜欢Js可选链?

更新日期: 2019-09-22阅读: 2.1k标签: 特性

很多 JavaScript 的特性极大地改变了你的编码方式。从 ES2015 及更高版本开始,对我的代码影响最大的功能是解构、箭头函数、类和模块系统。

截至2019年8月,一项新提案可选链(optional chaining)进入了第3阶段,将是一个很好的改进。可选的链接更改了从深层对象结构访问属性的方式。

让我们看看可选链是如何通过在深度访问可能缺少的属性时删除样板条件和变量来简化代码的。


1. 问题

由于 JavaScript 的动态特性,一个对象可以具有非常不同的对象嵌套结构。

通常,你可以在以下情况下处理此类对象:

  • 获取远程JSON数据
  • 使用配置对象
  • 具有可选属性

尽管这为对象提供了支持不同数据的灵活性,但是在访问此类对象的属性时,随之而来的是增加了复杂性。

bigObject 在运行时可以有不同的属性集:

// One version of bigObject
const bigObject = {
  // ...
  prop1: {
    //...
    prop2: {
      // ...
      value: 'Some value'
    }
  }
};

// Other version of bigObject
const bigObject = {
  // ...
  prop1: {
    // Nothing here   
  }
};

因此你必须手动检查属性是否存在:

// Later
if (bigObject && 
    bigObject.prop1 != null && 
    bigObject.prop1.prop2 != null) {
  let result = bigObject.prop1.prop2.value;
}

最好不要这样写,因为包含了太多的样板代码。。

让我们看看可选链是如何解决此问题,从而减少样板条件的。


2. 轻松深入访问属性

让我们设计一个保存电影信息的对象。该对象包含 title 必填属性,以及可选的 director 和 actor。

movieSmall 对象仅包含 title,而 movieFull 则包含完整的属性集:

const movieSmall = {
  title: 'Heat'
};

const movieFull = {
  title: 'Blade Runner',
  director: { name: 'Ridley Scott' },
  actors: [{ name: 'Harrison Ford' }, { name: 'Rutger Hauer' }]
};

让我们写一个获取导演姓名的函数。请注意 director 属性可能会丢失:

function getDirector(movie) {
  if (movie.director != null) {
    return movie.director.name;
  }
}

getDirector(movieSmall); // => undefined
getDirector(movieFull);  // => 'Ridley Scott'

if (movie.director) {...} 条件用于验证是否定义了 director 属性。如果没有这种预防措施,则在访问movieSmall 对象的导演的时,JavaScript 会引发错误 TypeError: Cannot read property 'name' of undefined。

这是用了可选链功能并删除 movie.director 存在验证的正确位置。新版本的 getDirector() 看起来要短得多:

function getDirector(movie) {
  return movie.director?.name;
}

getDirector(movieSmall); // => undefined
getDirector(movieFull);  // => 'Ridley Scott'

在 movie.director?.name 表达式中,你可以找到 ?.:可选链运算符。

对于 movieSmall,缺少属性 director。结果 movie.director?.name 的计算结果为 undefined。可选链运算符可防止引发 TypeError: Cannot read property 'name' of undefined 错误。

相反 movieFull 的属性 director是可用的。 movie.director?.name 默认被评估为 'Ridley Scott'。

简而言之,代码片段:

let name = movie.director?.name;

等效于:

let name;
if (movie.director != null) {
  name = movie.director.name;
}

?. 通过减少两行代码简化了 getDirector() 函数。这就是为什么我喜欢可选链的原因。

2.1 数组项

可选链能还可以做更多的事。你可以在同一表达式中自由使用多个可选链运算符。甚至可以用它安全地访问数组项!

下一个任务编写一个返回电影主角姓名的函数。

在电影对象内部,actor 数组可以为空甚至丢失,所以你必须添加其他条件:

function getLeadingActor(movie) {
  if (movie.actors && movie.actors.length > 0) {
    return movie.actors[0].name;
  }
}

getLeadingActor(movieSmall); // => undefined
getLeadingActor(movieFull);  // => 'Harrison Ford'

如果需要 if (movie.actors && movies.actors.length > 0) {...} ,则必须确保 movie 包含 actors 属性,并且该属性中至少有一个 actor。

使用可选链,这个任务就很容易解决:

function getLeadingActor(movie) {
  return movie.actors?.[0]?.name;
}

getLeadingActor(movieSmall); // => undefined
getLeadingActor(movieFull);  // => 'Harrison Ford'

actors?. 确保 actors 属性存在。 [0]?. 确保列表中存在第一个参与者。这真是个好东西!


3. 默认为Nullish合并

一项名为nullish 合并运算符的新提案会处理 undefined 或 null ,将其默认设置为特定值。

如果 variable 是 undefined 或 null,则表达式 variable ?? defaultValue 的结果为 defaultValue。否则,表达式的计算结果为 variable 值。

const noValue = undefined;
const value = 'Hello';

noValue ?? 'Nothing'; // => 'Nothing'
value   ?? 'Nothing'; // => 'Hello'

当链评估为 undefined 时,通过将默认值设置为零,Nullish 合并可以改善可选链。

例如,让我们更改 getLeading() 函数,以在电影对象中没有演员时返回 "Unknown actor" :

function getLeadingActor(movie) {
  return movie.actors?.[0]?.name ?? 'Unknown actor';
}

getLeadingActor(movieSmall); // => 'Unknown actor'
getLeadingActor(movieFull);  // => 'Harrison Ford'


4. 可选链的3种形式

你可以通过以下 3 种形式使用可选链。

第一种形式的 object.property 用于访问静态属性:

const object = null;
object?.property; // => undefined

第二种形式 object?.[expression] 用于访问动态属性或数组项:

const object = null;
const name = 'property';
object?.[name]; // => undefined
const array = null;
array?.[0]; // => undefined

最后,第三种形式 object?.([arg1, [arg2, ...]]) 执行一个对象方法:

const object = null;
object?.method('Some value'); // => undefined

如果需要,可以将这些形式组合起来以创建长的可选链:

const value = object.maybeUndefinedProp?.maybeNull()?.[propName];


5.短路:在null/undefined 处停止

可选链运算符的有趣之处在于,一旦在其左侧 leftHandSide?.rightHandSide 上遇到空值,就会停止对右侧访问器的评估。这称为短路。

看一个例子:

const nothing = null;
let index = 0;

nothing?.[index++]; // => undefined
index;              // => 0

nothing 保留一个空值,因此可选链立即求值为 undefined,并跳过右侧访问器的求值。因为 index 的值没有增加。


6. 何时使用可选链

要抵制使用可选链运算符访问任何类型属性的冲动:这会导致错误的用法。下一节将说明何时正确使用它。

6.1 可能无效的访问属性

必须仅在可能为空的属性附近使用 ?.: maybeNullish?.prop。在其他情况下,请使用老式的属性访问器:.property 或 [propExpression]。

调用电影对象。查看表达式 movie.director?.name,因为 director 可以是 undefined,所以在 director 属性附近使用可选链运算符是正确的。

相反,使用 ?. 访问电影标题 movie?.title 没有任何意义。电影对象不会是空的。

// Good
function logMovie(movie) {
  console.log(movie.director?.name);
  console.log(movie.title);
}

// Bad
function logMovie(movie) {
  // director needs optional chaining
  console.log(movie.director.name);

  // movie doesn't need optional chaining
  console.log(movie?.title);
}

6.2 通常有更好的选择

以下函数 hasPadding() 接受具有可选 padding 属性的样式对象。 padding 具有可选的属性 left,top,right,bottom。

尝试用可选链运算符:

function hasPadding({ padding }) {
  const top = padding?.top ?? 0;
  const right = padding?.right ?? 0;
  const bottom = padding?.bottom ?? 0;
  const left = padding?.left ?? 0;
  return left + top + right + bottom !== 0;
}

hasPadding({ color: 'black' });        // => false
hasPadding({ padding: { left: 0 } });  // => false
hasPadding({ padding: { right: 10 }}); // => true

虽然函数可以正确地确定元素是否具有填充,但是为每个属性使用可选链是毫无必要的。

更好的方法是使用对象散布运算符将填充对象默认为零值:

function hasPadding({ padding }) {
  const p = {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    ...padding
  };
  return p.top + p.left + p.right + p.bottom !== 0;
}

hasPadding({ color: 'black' });        // => false
hasPadding({ padding: { left: 0 } });  // => false
hasPadding({ padding: { right: 10 }}); // => true

我认为这一版本的 hasPadding() 可读性更好。


7. 我为什么喜欢它?

我喜欢可选链运算符,因为它允许轻松地从嵌套对象中访问属性。它可以防止编写针对访问者链中每个属性访问器上的空值进行验证的样板代码。

当可选链与空值合并运算符结合使用时,可以得到更好的结果,从而更轻松地处理默认值。

原文:https://dmitripavlutin.com/javascript-optional-chaining/
作者:Dmitri Pavlutin,翻译:疯狂的技术


链接: https://fly63.com/article/detial/6089

CSS新特性contain,控制页面的重绘与重排

contain 属性允许我们指定特定的 DOM 元素和它的子元素,让它们能够独立于整个 DOM 树结构之外。目的是能够让浏览器有能力只对部分元素进行重绘、重排,而不必每次都针对整个页面。

Html5、Css3、ES6的新特性

Html5的新特性语义化标签:有利于SEO,有助于爬虫抓取更多的有效信息,爬虫是依赖于标签来确定上下文和各个关键字的权重。表单新特性,多媒体视频(video)和音频(audio)

ES6新特性--var、let、const

var不存在块级作用域,具有变量提升机制。 let和const存在块级作用域,不存在变量提升。在同一作用域内只能声明一次。const在声明时需要赋值且无法修改,但如果常量是对象,则对象的属性可以修改。

Js即将到来的3个新特性

Optional Chaining(可选链式调用);Nullish coalescing(空值合并);Pipeline operator(管道运算符)通过三个函数对字符串进行处理;

Angular 8的新特性介绍

在今天早些时候Angular团队发布了8.0.0稳定版。其实早在NgConf 2019大会上,演讲者就已经提及了从工具到差分加载的许多内容以及更多令人敬畏的功能。下面是我对8.0.0一些新功能的简单介绍,希望可以帮助大家快速了解新版本

使用 React 要懂的 Js特性

与我使用的其他框架相比,我最喜欢 React 的原因之一就是它对 JavaScript 的暴露程度。没有模板DSL( JSX 编译为合理的 JavaScript),组件 API 只是通过添加 React Hooks 变得更简单,并且该框架为解决的核心 UI 问题提供非常少的抽象概念

ES2019 新特性汇总

最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~另外之前也整理了 《ES6/ES7/ES8/ES9系列》,可以一起看哈。

Js的用途和特性

JavaScript 最初的目的是为了“赋予网页生命”。这种编程语言我们称之为脚本。它们可以写在 HTML 中,在页面加载的时候会自动执行。脚本作为纯文本存在和执行。它们不需要特殊的准备或编译即可运行。

十个超级实用的 JS 特性

你可能刚上手 JavaScript,或者只是曾经偶尔用过。不管怎样,JavaScript 改变了很多,有些特性非常值得一用。 这篇文章介绍了一些特性,在我看来,一个严肃的 JavaScript 开发者每天都多多少少会用到这些特性

解密HTTP/2与HTTP/3 的新特性

HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!