游乐园

游乐园

学习练习

简介

与许多其他语言不同,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

可选链

如果你想获取对象中的嵌套值,但父级键并不存在,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 或 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 日访问)。

说明

你的工作是开发和维护游乐园 IT 系统的部分功能。具体来说,你负责管理游客和门票的程序。

首先,你要在系统中创建一位新游客。然后,你要提供一个用于撤销门票的函数。在第 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,全部免费。