ES新提案:雙問號操做符

做者:前端小智html

譯者:Dr. Axel Rauschmayer前端

來源:2alitygit


阿里雲最近在作活動,低至2折,有興趣能夠看看promotion.aliyun.com/ntms/yunpar…github


爲了保證的可讀性,本文采用意譯而非直譯。express

本文主要講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, 不然返回 bspa

這使得使用||指定一個默認值成爲可能,若是實際值是假的,那麼將使用這個默認值:

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

交流(歡迎加入羣,羣工做日都會發紅包,互動討論技術)

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/qq449245884…

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

相關文章
相關標籤/搜索