遊樂園

遊樂園

學習練習

簡介

與許多其他語言不同,JavaScript 裡有兩個不同的實體,用來表示(有意義的)值不存在。 它們分別是null與undefined。

Null

原始值null用於刻意表示任何型別變數的「空值」。

let name = null;
// name is intentionally set to "empty" because it is not
// available

你可以用嚴格相等運算子===來檢查一個變數是否為null。

let name = null;

name === null;
// => true

Undefined

尚未被指定值的變數,其型別為undefined。1

這表示null代表的是一個空值(但仍然是一個值),而undefined則代表完全沒有值。🤯

undefined會出現在不同的情境中。

  • 如果一個變數在宣告時(初始化)沒有給值,它就是undefined。
  • 如果你試圖存取物件中不存在的鍵所對應的值,會得到undefined。
  • 如果函式沒有回傳值,結果就是undefined。
  • 如果呼叫函式時沒有傳入引數,該引數就是undefined,除非這個引數有預設值。
let name;
console.log(name);
// => undefined

你可以使用嚴格相等運算子===來檢查一個變數是否為undefined。

let name;

name === undefined;
// => true

Optional Chaining

如果你想取得物件中巢狀的值,但父層的鍵不存在,JavaScript 會拋出錯誤。 為了輕鬆避免這種情況,選擇性鏈結在 2020 年被加入語言規格中。 使用選擇性鏈結運算子?.,你可以確保 JavaScript 只在父層不是null或undefined時,才嘗試存取巢狀的鍵。 否則,它會回傳undefined。

const obj = {
  address: {
    street: 'Trincomalee Highway',
    city: 'Batticaloa',
  },
};

obj.address.zipCode;
// => undefined

obj.residence.street;
// => TypeError: Cannot read property 'street' of undefined

obj.residence?.street;
// => undefined

Nullish Coalescing

有些情況下,你會想在變數為 null 或 undefined 時套用預設值(而且只在這種時候)。 為了處理這個問題,空值合併運算子??在 2020 年被導入。 它只會在左側運算元是null或undefined時回傳右側運算元。 否則,它會回傳左側運算元。

let amount = null;
amount = amount ?? 1;
// => 1

amount = 0;
amount = amount ?? 1;
// => 0

[1] Undefined, MDN. (2021). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined (取用於 2021 年 6 月 4 日)。

說明

你的工作是提供並維護遊樂園資訊系統的部分功能。特別是,你負責管理訪客與票券的程式。

首先,你會在系統中建立一位新訪客。接著,你會提供一個用來註銷票券的函式。在第 3 和第 4 項任務中,你會處理票券追蹤系統,它能協助防止偽造。最後,你會協助讓系統配合新的法律要求。

1. 建立新訪客

訪客第一次來到遊樂園時,需要在終端機輸入姓名與年齡,並同意條款與細則,才能完成註冊。當然,他們還需要買一張票券。每張票券都有一個像H32AZ123這樣的識別碼。

撰寫一個createVisitor函式,它接受三個引數。

  • 訪客的姓名。
  • 訪客的年齡。
  • 訪客所購買票券的識別碼。

這個函式應回傳一個包含上述資訊的物件。物件中的鍵應命名為name、age和ticketId。

createVisitor('Verena Nardi', 45, 'H32AZ123');
// => { name: 'Verena Nardi', age: 45, ticketId: 'H32AZ123' }

2. 註銷票券

當人們離開遊樂園時,他們的票券會被註銷。如果他們再回來,就需要買一張新的票券。為了節省常客的時間,他們只需要註冊一次,訪客資訊會保留到之後的來訪。

這表示當訪客離開園區時,只有他們的票券應該被註銷,而訪客物件的其餘部分應保持不變。實作revokeTicket函式,它接受一個訪客物件,將票券識別碼設為null,然後回傳該物件。

const visitor = {
  name: 'Verena Nardi',
  age: 45,
  ticketId: 'H32AZ123',
};

revokeTicket(visitor);
// => { name: 'Verena Nardi', age: 45, ticketId: null }

3. 判斷票券狀態

為了防止偽造,票券識別碼是獨一無二的。票券一經印出,其識別碼就會被加入系統中某個物件的鍵,以便追蹤。

在票券賣給訪客之前,會以null值儲存在票券追蹤物件中。當票券賣給訪客時,會把訪客姓名指定為其值。當員工對票券的有效性有疑問時,他們需要在系統中查詢該票券的狀態。

實作ticketStatus函式,它接受追蹤物件與票券識別碼作為引數。它應回傳下列其中一種結果。

  • 如果在追蹤物件中找不到該識別碼,回傳'unknown ticket id'
  • 如果票券已印出但尚未售出,回傳'not sold'
  • 如果票券已售出,回傳'sold to {name}',其中{name}是訪客的姓名
const tickets = {
  '0H2AZ123': null,
  '23LA9T41': 'Verena Nardi',
};

ticketStatus(tickets, 'RE90VAW7');
// => 'unknown ticket id'

ticketStatus(tickets, '0H2AZ123');
// => 'not sold'

ticketStatus(tickets, '23LA9T41');
// => 'sold to Verena Nardi'

4. 改良票券狀態的回應

過了一陣子,你收到員工的回饋,他們希望票券狀態能更容易一眼看懂。他們只想看到訪客姓名,或是票券無效的提示。

撰寫simpleTicketStatus函式,它接受與第 3 項任務中ticketStatus相同的引數。這個函式只會回傳兩種不同結果的其中一種。

  • 如果票券已售出,回傳訪客的姓名
  • 如果票券尚未售出,或在追蹤物件中找不到該識別碼,回傳'invalid ticket !!!'
const tickets = {
  '0H2AZ123': null,
  '23LA9T41': 'Verena Nardi',
};

simpleTicketStatus(tickets, '23LA9T41');
// => 'Verena Nardi'

simpleTicketStatus(tickets, '0H2AZ123');
// => 'invalid ticket !!!'

simpleTicketStatus(tickets, 'RE90VAW7');
// => 'invalid ticket !!!'

5. 判斷條款與細則的版本

由於新的法律要求,新建的訪客物件現在也包含使用者所同意的「一般條款與細則」(GTC)的詳細資訊。你可以在下方看到新訪客物件的範例。

遊樂園的收銀員現在需要確認訪客是否需要簽署新版本的 GTC。為此,實作gtcVersion函式,它接受一個訪客物件作為引數,並在 GTC 版本資訊存在時回傳該版本。如果沒有版本資訊,則不應回傳任何東西。

const visitorNew = {
  name: 'Verena Nardi',
  age: 45,
  ticketId: 'H32AZ123',
  gtc: {
    signed: true,
    version: '2.1',
  },
};

gtcVersion(visitorNew);
// => '2.1'

const visitorOld = {
  name: 'Verena Nardi',
  age: 45,
  ticketId: 'H32AZ123',
};

gtcVersion(visitorOld);
// => undefined
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
JavaScript Exercism

準備好開始 遊樂園 了嗎?

註冊 Exercism,透過 37 個概念159 個練習 和真人引導來學習並精通 JavaScript,全部免費。