Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS可选链的好处 #100

Open
husky-dot opened this issue Aug 26, 2019 · 0 comments
Open

JS可选链的好处 #100

husky-dot opened this issue Aug 26, 2019 · 0 comments

Comments

@husky-dot
Copy link
Owner

作者:Dmitri Pavlutin
译者:前端小智
来源:dmitripavlutin

为了保证的可读性,本文采用意译而非直译。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

JS的一些特性极大地改变了咱们的编码方式。从ES6年开始,对咱们代码影响最大的特性的解 、箭头函数、类和模块系统。

到2019年8月,一个新的可选链提案已经进入第三阶段,这是一个很好的改进。可选链接改变了从深层对象结构访问属性的方式。

来看看这是又是什么骚操作。

这个礼拜《大迁世界》有抽奖活动,奖品:专栏 《左耳听风》 x3, 技术书 x5,欢迎关注回复:抽奖

问题

由于JS的动态特性,对象可以具有多层不同的嵌套对象结构。

通常,当咱们处理以下这些对象时:

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

虽然JS为对象支持不同层次数据结构,但是在访问此类对象的属性时,复杂性也随着增加。

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

// 嵌套版本
const bigObject = {
  // ...
  prop1: {
    //...
    prop2: {
      // ...
      value: 'Some value'
    }
  }
};

// 简单版本
const bigObject = {
  // ...
  prop1: {
    // Nothing here   
  }
};

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

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

这样写太过冗长了,最好避免写它。

咱们来看看可选链如何解决这个问题,以减少冗余的代码。

2. 易于深入访问属性

设计一个保存电影信息的对象。 该对象包含必填title属性,以及可选的directoractors

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

const movieSmall = {
  title: 'Heat'
};

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

写一个获取director的函数。 请记住,director 可能不存在。

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

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

if(movie.director){...}条件用于验证是否定义了director属性。 如果没有这个预防措施,在访问movieSmall对象的director时,JS会抛出TypeError: Cannot read property 'name' of undefined

这种场景最适合使用可选链的功能了,如下所示,代码将简洁很多。

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

简单地说,代码片段:

let name = movie.director?.name;

等价于

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

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

2.1 数组项

可选的链功能可以做得更多。可以自由地在同一个表达式中使用多个可选的链接操作符,甚至可以使用它安全地访问数组项。

下一个任务是编写一个函数,返回电影的actors中的name

movie对象中,actors数组可以是空的,甚至是缺失的,因此必须添加额外的条件来判空。

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]?.确保列表中存在第一个actor

3.nullish coalescing (空值合并?)

一个名为nullish coalescing operator的新提议? 处理undefinednull,将它们默认为特定值。

表达式变量?? 如果变量undefined或为null,则默认值为指定的值。

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

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

接着使用??来优化一下 getLeading()函数,当movie对象中没有actor时返回“Unknown actor

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

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

4. 可选链的三种形式

咱们可以使用以下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

6. 何时使用可选链

不要急于使用可选的链操作符来访问任何类型的属性:这会导致错误的使用。

6.1访问潜在无效的属性

?.一般使用在可能为空的属性:maybeNullish?.prop。在确定属性不为空的情况下,使用属性访问器:.property或[propExpression]

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

// 不好
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具有lefttoprightbottom可选属性。

尝试使用可选的链操作符:

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

虽然函数正确地确定了元素是否有padding,但是对于每个属性使用可选的链有点过于麻烦了。

更好的方法是使用对象扩展操作符将padding对象默认为零值

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

这个就比可选链来的更简洁。

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

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq449245884/xiaozhi

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant