<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          重新審視 undefined 和 null

          共 11334字,需瀏覽 23分鐘

           ·

          2021-03-15 06:42

          • 本文已獲得原作者的獨(dú)家授權(quán),有想轉(zhuǎn)載的朋友們可以在后臺(tái)聯(lián)系我申請(qǐng)開(kāi)白哦!
          • PS:歡迎掘友們向我投稿哦,被采用的文章還可以送你掘金精美周邊!

          • 原文地址:undefined vs. null revisited
          • 原文作者:Dr. Axel Rauschmayer
          • 譯文出自:掘金翻譯計(jì)劃
          • 本文永久鏈接:https://github.com/xitu/gold-miner/blob/master/article/2021/undefined-null-revisited.md
          • 譯者:霜羽 Hoarfroster
          • 校對(duì)者:Moonball、felixliao

          重新審視 undefined 和 null

          很多的編程語(yǔ)言都有一種表示空值的類型,叫做 null。它指示了一個(gè)變量當(dāng)前并沒(méi)有指向任何對(duì)象 —— 例如,某個(gè)變量還沒(méi)有初始化的時(shí)候。

          作為不同,JavaScript 則擁有兩種表示空值的類型,一種是 undefined,另一種則是 null。在這篇文章中,我們將測(cè)試它們的區(qū)別,以及如何去挑選最佳的類型或避免去使用它們。

          undefined vs. null

          兩個(gè)值都很是相像,并且通常被相互替代著使用,也因此,他們之間的區(qū)別很是細(xì)微。

          undefinednull 在ECMAScript 語(yǔ)言標(biāo)準(zhǔn)上的對(duì)比

          ECMAScript 語(yǔ)言標(biāo)準(zhǔn)按照如下內(nèi)容描述他們:

          • undefined 是在一個(gè)變量還沒(méi)有被賦值時(shí)候使用的。出處
          • null 表示任何有意地缺省對(duì)象值。出處

          我們等下就會(huì)探索一下作為程序員,我們應(yīng)該如何去以最佳的方式使用這兩個(gè)值。

          兩個(gè)空值 —— 一個(gè)不能彌補(bǔ)的錯(cuò)誤

          在 JavaScript 中同時(shí)有兩個(gè)表示空值的值現(xiàn)在被認(rèn)為是一個(gè)設(shè)計(jì)錯(cuò)誤(哪怕是 JavaScript 之父 Brendan Eich 也這么認(rèn)為)。

          那么為什么不從 JavaScript 中刪除這兩個(gè)值之一呢?JavaScript 的一項(xiàng)核心原則是永不破壞向后的兼容性。該原則具有好處,但同時(shí)也擁有著最大的缺點(diǎn),即無(wú)法彌補(bǔ)設(shè)計(jì)錯(cuò)誤。

          undefinednull 的歷史

          在 Java(影響了 JavaScript 很多方面的語(yǔ)言)中初始值依賴于一個(gè)變量的靜態(tài)類型:

          • 以對(duì)象值為類型的變量初始化為 null
          • 每個(gè)基本類型都擁有它的初始值,例如 int 整型對(duì)應(yīng) 0

          在 JavaScript 中,每一個(gè)變量都可以存儲(chǔ)對(duì)象值或原始值,意味著如果 null 表示不是一個(gè)對(duì)象,那么 JavaScript 也同時(shí)需要一個(gè)初始值表示既不是一個(gè)對(duì)象也不擁有原始值,這就是 undefined

          undefined 的出現(xiàn)場(chǎng)合

          如果一個(gè)變量 myVar 還沒(méi)有被初始化,那么它的值就是 undefined

          let myVar;
          assert.equal(myVar, undefined);

          如果一個(gè)屬性 .unknownProp 不存在,訪問(wèn)這個(gè)屬性就會(huì)生成 undefined 值:

          const obj = {};
          assert.equal(obj.unknownProp, undefined);

          如果一個(gè)函數(shù)沒(méi)有明確返回任何內(nèi)容,那么默認(rèn)就會(huì)返回 undefined

          function myFunc({
          }

          assert.equal(myFunc(), undefined);

          如果一個(gè)函數(shù)擁有一個(gè) return 語(yǔ)句但沒(méi)有指定任何返回值,那么也會(huì)默認(rèn)返回 undefined

          function myFunc({
              return;
          }

          assert.equal(myFunc(), undefined);

          如果一個(gè)參數(shù) x 沒(méi)有傳實(shí)參,那么就會(huì)被初始化為 undefined

          function myFunc(x{
              assert.equal(x, undefined);
          }

          myFunc();

          通過(guò) obj?.someProp 訪問(wèn)的可選鏈在objundefinednull 的時(shí)候返回 undefined

          > undefined?.someProp
          undefined
          > null?.someProp
          undefined

          null 的出現(xiàn)場(chǎng)合

          一個(gè)對(duì)象的原型要么是另一個(gè)對(duì)象,要么是原型鏈末尾的 nullObject.prototype 沒(méi)有原型:

          > Object.getPrototypeOf(Object.prototype)
          null

          如果我們使用一個(gè)正則表達(dá)式(例如 /a/)匹配一個(gè)字符串(例如 x),我們要么得到一個(gè)存儲(chǔ)著匹配數(shù)據(jù)的對(duì)象(如果匹配成功),要么得到 null(如果匹配失敗)。

          > /a/.exec('x')
          null

          JSON 數(shù)據(jù)格式 不支持 undefined,只支持 null

          > JSON.stringify({a: undefined, b: null})
          '{"b":null}'

          專門用來(lái)對(duì)付 undefinednull 的操作符

          undefined 以及默認(rèn)參數(shù)值

          一個(gè)參數(shù)的默認(rèn)值會(huì)在以下情況下被使用:

          • 這個(gè)參數(shù)被我們忽略掉了。
          • 這個(gè)參數(shù)被賦予 undefined 值。

          舉個(gè)例子:

          function myFunc(arg = 'abc'{
              return arg;
          }

          assert.equal(myFunc('hello'), 'hello');
          assert.equal(myFunc(), 'abc');
          assert.equal(myFunc(undefined), 'abc');

          當(dāng)指向它的值為一個(gè)元值時(shí),undefined 也會(huì)觸發(fā)默認(rèn)參數(shù)值。

          以下的例子示范了這個(gè)特性有用的地方:

          function concat(str1 = '', str2 = ''{
              return str1 + str2;
          }

          function twice(str// (A)
              return concat(str, str);
          }

          在 A 行,我們并沒(méi)有制定參數(shù) str 的默認(rèn)值,而當(dāng)這個(gè)參數(shù)被忽略掉的時(shí)候,我們將該狀態(tài)轉(zhuǎn)發(fā)到 concat(),讓其選擇默認(rèn)值。

          undefined,解構(gòu)默認(rèn)值

          解構(gòu)下的默認(rèn)值的工作方式與參數(shù)默認(rèn)值類似 —— 如果變量在數(shù)據(jù)中不匹配或與 undefined 匹配,則使用它們:

          const [a = 'a'] = [];
          assert.equal(a, 'a');

          const [b = 'b'] = [undefined];
          assert.equal(b, 'b');

          const {prop: c = 'c'} = {};
          assert.equal(c, 'c');

          const {prop: d = 'd'} = {propundefined};
          assert.equal(d, 'd');

          undefinednull 和可選鏈

          如果通過(guò) value?.prop 使用了可選鏈:

          • 如果 valueundefinednull 的,將會(huì)返回 undefined。也就是說(shuō),如果 value.prop 拋出錯(cuò)誤,就會(huì)返回 undefined
          • 否則會(huì)返回 value.prop.
          function getProp(value{
              // 可選的靜態(tài)屬性訪問(wèn)
              return value?.prop;
          }

          assert.equal(
              getProp({prop123}), 123);
          assert.equal(
              getProp(undefined), undefined);
          assert.equal(
              getProp(null), undefined);

          以下的兩個(gè)操作也很是類似的工作:

          obj?.[?expr?] // 可選的動(dòng)態(tài)屬性訪問(wèn)
          func?.(?arg0?, ?arg1?) // 可選的函數(shù)或方法調(diào)用

          undefinednull 和空合并

          空合并操作符 ?? 可讓我們?cè)谝粋€(gè)值是 undefinednull 時(shí),使用默認(rèn)值:

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

          > 0 ?? 'default value'
          0
          > 123 ?? 'default value'
          123
          > '' ?? 'default value'
          ''
          > 'abc' ?? 'default value'
          'abc'

          空合并賦值操作符 ??= 合并了空合并操作符與賦值操作符:

          function setName(obj{
              obj.name ??= '(Unnamed)';
              return obj;
          }

          assert.deepEqual(
              setName({}),
              {name'(Unnamed)'}
          );
          assert.deepEqual(
              setName({nameundefined}),
              {name'(Unnamed)'}
          );
          assert.deepEqual(
              setName({namenull}),
              {name'(Unnamed)'}
          );
          assert.deepEqual(
              setName({name'Jane'}),
              {name'Jane'}
          );

          處理 undefinednull

          以下的部分解釋了在我們代碼中最常見(jiàn)的處理 undefinednull 的方法:

          實(shí)際值既不是 undefined 也不是 null

          例如,我們可能希望屬性 file.title 始終存在并且始終是字符串,那么有兩種常見(jiàn)的方法可以實(shí)現(xiàn)此目的。

          請(qǐng)注意,在此博客文章中,我們僅檢查 undefinednull,而不檢查值是否為字符串。你需要自己決定是否要添加檢查器,作為附加的安全保障措施。

          同時(shí)禁止 undefinednull

          例如:

          function createFile(title{
              if (title === undefined || title === null) {
                  throw new Error('`title` must not be nullish');
              }
              // ···
          }

          為什么選擇這個(gè)方法?

          • 我們希望以相同的方式處理 undefinednull,因?yàn)?JavaScript 代碼就是經(jīng)常那樣做,例如:

            // 檢查一個(gè)屬性是否存在
            if (!obj.requiredProp) {
              obj.requiredProp = 123;
            }

            // 通過(guò)空合并操作符使用默認(rèn)值
            const myValue = myParameter ?? 'some default';

          • 如果我們的代碼中出現(xiàn)了問(wèn)題,讓 undefinednull 出現(xiàn)了,我們需要讓它盡早結(jié)束執(zhí)行并拋出錯(cuò)誤。

          同時(shí)對(duì) undefinednull 使用默認(rèn)值

          例如:

          function createFile(title{
              title ??= '(Untitled)';
              // ···
          }

          我們不能使用參數(shù)默認(rèn)值,因?yàn)樗粫?huì)被 undefined 觸發(fā)。在這里,我們依賴于空合并賦值運(yùn)算符 ??=

          為什么選擇這個(gè)方法?

          • 我們希望以相同方式對(duì)待 undefinednull(見(jiàn)上文)。
          • 我們希望我們的代碼無(wú)聲但有力地對(duì)待 undefinednull

          undefinednull 是一個(gè)被忽略的值

          例如,我們可能希望屬性 file.title 是字符串或是被忽略的值(即 file 沒(méi)有標(biāo)題),那么有幾種方法可以實(shí)現(xiàn)此目的。

          null 是被忽略值

          例如:

          function createFile(title{
              if (title === undefined) {
                  throw new Error('`title` 不應(yīng)該是 undefined');
              }
              return {title};
          }

          或者,undefined 也可以觸發(fā)默認(rèn)值:

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

          為什么要選擇這個(gè)方法?

          • 我們需要一個(gè)空值來(lái)表示被忽略。
          • 我們不希望空值觸發(fā)參數(shù)默認(rèn)值并破壞默認(rèn)值。
          • 我們想將空值字符串化為 JSON(這是我們無(wú)法對(duì) undefined 進(jìn)行的處理)。

          undefined 是被忽略的值

          例如:

          function createFile(title{
              if (title === null) {
                  throw new Error('`title` 不應(yīng)該是 null');
              }
              return {title};
          }

          為什么選擇這種方法?

          • 我們需要一個(gè)空值來(lái)表示被忽略。
          • 我們確實(shí)希望空值觸發(fā)參數(shù)或解構(gòu)默認(rèn)值。

          undefined 的一個(gè)缺點(diǎn)是它通常是在 JavaScript 中意外賦予的 —— 在未初始化的變量,屬性名稱中的錯(cuò)字,忘記從函數(shù)中返回內(nèi)容等。

          為什么不同時(shí)將 undefinednull 看作是被忽略的值?

          當(dāng)接收到一個(gè)值時(shí),將 undefinednull 都視為 “空值” 是有意義的。但是,當(dāng)我們創(chuàng)建值時(shí),我們不希望模棱兩可,以避免不必要的麻煩。

          這指向了另一種角度:如果我們需要一個(gè)被忽略的值,但又不想使用 undefinednull 作為被忽略值時(shí)該怎么辦?看看下文吧:

          其他處理被忽略值的方法

          特殊值

          我們可以創(chuàng)建一個(gè)特殊值,每當(dāng)屬性被忽略時(shí) .title 時(shí)就使用該值:

          const UNTITLED = Symbol('UNTITLED');
          const file = {
              title: UNTITLED,
          };

          Null 對(duì)象模式

          Null 對(duì)象模式 來(lái)自 OOP(面對(duì)對(duì)象編程):

          • 一個(gè)公共超類的所有子類都具有相同的接口。
          • 每個(gè)子類實(shí)現(xiàn)一種不同的模式供其實(shí)例使用。
          • 這些模式之一是 null

          在下文中,UntitledFile 繼承了 “null” 模式。

          // Abstract superclass
          class File {
              constructor(content) {
                  if (new.target === File) {
                      throw new Error('Can’t instantiate this class');
                  }
                  this.content = content;
              }
          }

          class TitledFile extends File {
              constructor(content, title) {
                  super(content);
                  this.title = title;
              }

              getTitle() {
                  return this.title;
              }
          }

          class UntitledFile extends File {
              constructor(content) {
                  super(content);
              }

              getTitle() {
                  return '(Untitled)';
              }
          }

          const files = [
              new TitledFile('Dear diary!''My Diary'),
              new UntitledFile('Reminder: pick a title!'),
          ];

          assert.deepEqual(
              files.map(f => f.getTitle()),
              [
                  'My Diary',
                  '(Untitled)',
              ]);

          我們也可以只為標(biāo)題(而不是整個(gè)文件對(duì)象)使用空對(duì)象模式。

          “也許”類型

          “也許”類型是一種函數(shù)編程技術(shù):

          function getTitle(file{
              switch (file.title.kind) {
                  case 'just':
                      return file.title.value;
                  case 'nothing':
                      return '(Untitled)';
                  default:
                      throw new Error();
              }
          }

          const files = [
              {
                  title: {kind'just'value'My Diary'},
                  content'Dear diary!',
              },
              {
                  title: {kind'nothing'},
                  content'Reminder: pick a title!',
              },
          ];

          assert.deepEqual(
              files.map(f => getTitle(f)),
              [
                  'My Diary',
                  '(Untitled)',
              ]);

          我們本可以通過(guò)數(shù)組對(duì) "just" 和 "nothing" 進(jìn)行編碼,但我們的方法的好處是 TypeScript 對(duì)其有很好的支持(通過(guò)可辨識(shí)聯(lián)合)。

          我的方法

          我不喜歡將 undefined 用作被忽略的值的原因有三個(gè):

          • undefined 通常是在 JavaScript 中意外出現(xiàn)的。
          • undefined 會(huì)觸發(fā)參數(shù)和解構(gòu)的默認(rèn)值(出于某些原因,某些人更喜歡 undefined)。

          因此,如果需要特殊值,可以使用以下兩種方法之一:

          • 我將 null 用作被忽略的值。(順便說(shuō)一句,TypeScript 相對(duì)較好地支持了這種方法。)
          • 我通過(guò)上述的其中一種技術(shù)避免了同時(shí)出現(xiàn) undefinednull 的情況,優(yōu)點(diǎn)在乎讓代碼更干凈,而缺點(diǎn)在于需要做出更多的工作。

          • 本文正在參與「掘金 2021 春招闖關(guān)活動(dòng)」, 點(diǎn)擊查看活動(dòng)詳情

          如果發(fā)現(xiàn)譯文存在錯(cuò)誤或其他需要改進(jìn)的地方,歡迎到 掘金翻譯計(jì)劃 對(duì)譯文進(jìn)行修改并 PR,也可獲得相應(yīng)獎(jiǎng)勵(lì)積分。文章開(kāi)頭的 本文永久鏈接 即為本文在 GitHub 上的 MarkDown 鏈接。


          掘金翻譯計(jì)劃 是一個(gè)翻譯優(yōu)質(zhì)互聯(lián)網(wǎng)技術(shù)文章的社區(qū),文章來(lái)源為 掘金 上的英文分享文章。內(nèi)容覆蓋 Android、iOS、前端、后端、區(qū)塊鏈、產(chǎn)品、設(shè)計(jì)、人工智能等領(lǐng)域,想要查看更多優(yōu)質(zhì)譯文請(qǐng)持續(xù)關(guān)注 掘金翻譯計(jì)劃、官方微博、知乎專欄。

          最后





          如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「huab119」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          3. 關(guān)注公眾號(hào)「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。






          點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲艾薇在线观看 | 女人18AV| 青娱乐久久91 | 免费无码又爽又高潮视频 | 在线午夜 |