<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

          共 11063字,需瀏覽 23分鐘

           ·

          2021-03-30 17:31

          點(diǎn)擊上方“前端簡報(bào)”,選擇“設(shè)為星標(biāo)

          第一時(shí)間關(guān)注技術(shù)干貨!


          • 本文已獲得原作者的獨(dú)家授權(quán),有想轉(zhuǎn)載的朋友們可以在后臺(tái)聯(lián)系我申請(qǐng)開白哦!
          • 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

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

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

          undefined vs. null

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

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

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

          • undefined 是在一個(gè)變量還沒有被賦值時(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),即無法彌補(bǔ)設(shè)計(jì)錯(cuò)誤。

          undefinednull 的歷史

          在 Java(影響了 JavaScript 很多方面的語言)中初始值依賴于一個(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 還沒有被初始化,那么它的值就是 undefined

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

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

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

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

          function myFunc({
          }

          assert.equal(myFunc(), undefined);

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

          function myFunc({
              return;
          }

          assert.equal(myFunc(), undefined);

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

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

          myFunc();

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

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

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

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

          > 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}'

          專門用來對(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 行,我們并沒有制定參數(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 和可選鏈

          如果通過 value?.prop 使用了可選鏈:

          • 如果 valueundefinednull 的,將會(huì)返回 undefined。也就是說,如果 value.prop 拋出錯(cuò)誤,就會(huì)返回 undefined
          • 否則會(huì)返回 value.prop.
          function getProp(value{
              // 可選的靜態(tài)屬性訪問
              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)屬性訪問
          func?.(?arg0?, ?arg1?) // 可選的函數(shù)或方法調(diào)用

          undefined、null 和空合并

          空合并操作符 ?? 可讓我們?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

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

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

          例如,我們可能希望屬性 file.title 始終存在并且始終是字符串,那么有兩種常見的方法可以實(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;
            }

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

          • 如果我們的代碼中出現(xià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(見上文)。
          • 我們希望我們的代碼無聲但有力地對(duì)待 undefinednull。

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

          例如,我們可能希望屬性 file.title 是字符串或是被忽略的值(即 file 沒有標(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è)空值來表示被忽略。
          • 我們不希望空值觸發(fā)參數(shù)默認(rèn)值并破壞默認(rèn)值。
          • 我們想將空值字符串化為 JSON(這是我們無法對(duì) undefined 進(jìn)行的處理)。

          undefined 是被忽略的值

          例如:

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

          為什么選擇這種方法?

          • 我們需要一個(gè)空值來表示被忽略。
          • 我們確實(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ì)象模式 來自 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)',
              ]);

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

          我的方法

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

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

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

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

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

          瀏覽 53
          點(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>
                  黑人鸡巴视频 | 天天操天天日天天射 | 亚州日韩AV中文字幕 | 男人天堂网在线观看 | 超黄网站 |