遊園地

遊園地

学習演習

はじめに

他の多くのプログラミング言語とは異なり、JavaScriptには、意味のある値が存在しないことを表すものが2つあります。 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

オプショナルチェイニング

オブジェクトの入れ子になった値を取得しようとしたとき、親のキーが存在しないと、JavaScriptはエラーを投げます。 これを手軽に避けるために、2020年にオプショナルチェイニングが言語仕様に追加されました。 オプショナルチェイニング演算子?.を使うと、親がnullやundefinedでない場合にだけ、JavaScriptが入れ子のキーにアクセスしようとするようにできます。 そうでなければ、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

Null合体演算子

変数がnullやundefinedの場合にだけ、デフォルト値を適用したいという場面があります。 これに対応するために、2020年にNull合体演算子??が導入されました。 左側のオペランドが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日閲覧).

説明

遊園地のITシステムの一部を提供し、維持する仕事を担当します。 特に、来園者とチケットを管理するプログラムを担当します。

まず、システムに新しい来園者を登録します。 次に、チケットを無効にする関数を作成します。 タスク3と4では、偽造を防ぐのに役立つチケット追跡システムに取り組みます。 最後に、新しい法的要件に合わせてシステムを適応させる手助けをします。

1. 新しい来園者を作成する

来園者が初めて遊園地に来るときは、ターミナルで名前と年齢を入力し、利用規約に同意して登録する必要があります。 もちろん、チケットも購入する必要があります。 各チケットにはH32AZ123のような識別子があります。

3つの引数を取る関数createVisitorを書きましょう。

  • 来園者の名前。
  • 来園者の年齢。
  • 来園者が購入したチケットの識別子。

関数は、この情報を保持するオブジェクトを返す必要があります。 オブジェクトのキーはname、age、ticketIdとします。

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

2. チケットを無効にする

来園者が遊園地を去るとき、そのチケットは無効になります。 また来る場合は、新しいチケットを購入する必要があります。 常連の来園者の手間を省くため、登録は一度だけでよく、来園者の情報は次回以降の来園のために保持されます。

つまり、来園者が遊園地を去るときに無効になるのはチケットだけで、来園者オブジェクトの残りの部分はそのまま残ります。 来園者オブジェクトを受け取り、チケットの識別子をnullに設定して、そのあとでオブジェクトを返す関数revokeTicketを実装しましょう。

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. チケットの状態の応答を改善する

しばらくすると、従業員から、チケットの状態をひと目でわかりやすくしてほしいというフィードバックが届きます。 従業員は、来園者の名前か、チケットが無効であるかのどちらかだけを知りたいと思っています。

タスク3のticketStatusと同じ引数を取る関数simpleTicketStatusを書きましょう。 この関数は、2つの結果のうちのどちらか一方だけを返します。

  • チケットが販売された場合は、来園者の名前
  • チケットがまだ販売されていない場合、または識別子が追跡オブジェクトに見つからなかった場合は'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に署名する必要があるかどうかを確認する必要があります。 そのために、来園者オブジェクトを引数として受け取り、GTCのバージョンが利用可能であればそれを返す関数gtcVersionを実装しましょう。 バージョン情報が利用できない場合は、何も返しません。

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を学んでマスターできます。すべて無料です。