深入理解红宝书(17)

204 阅读5分钟

第五章 基本引用类型

这是我参与8月更文挑战的第17天,活动详情查看: 8月更文挑战

不知不觉已经更到第五章了,xdm继续加油!今天讲基本引用类型中的Date,是一个我们在日常的工作中出现频流非常高的类型。第五章开始就要接触对象这个概念,对我们以后的学习非常重要,一定要理解透彻

本章内容

  • 理解对象
  • 基本 JavaScript 数据类型
  • 原始值与原始值包装类型

引用值(或者对象)是某个特定引用类型的实例。在 ECMAScript 中,引用类型是把数据和功能组织到一起的结构,经常被人错误地称作“类”。虽然从技术上讲 JavaScript 是一门面向对象语言,但 ECMAScript 缺少传统的面向对象编程语言所具备的某些基本结构,包括类和接口。引用类型有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。

注意 引用类型虽然有点像类,但跟类并不是一个概念。为避免混淆,本章后面不会使用 术语“类”

对象被认为是某个特定引用类型的实例。新对象通过使用 new 操作符后跟一个构造函数(constructor) 来创建。构造函数就是用来创建新对象的函数,比如下面这行代码:

let now = new Date();

这行代码创建了引用类型 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象。ECMAScript 提供了很多像 Date 这样的原生引用类型,帮助开发者实现常见的任务。

注意 函数也是一种引用类型,但有关函数的内容太多了,一章放不下,所以本书专门用 第 10 章来介绍函数。

5.1 Date

ECMAScript 的 Date 类型参考了 Java 早期版本中的 java.util.Date。为此,Date 类型将日期 保存为自协调世界时(UTC,Universal Time Coordinated)时间 1970 年 1 月 1 日午夜(零时)至今所 经过的毫秒数。使用这种存储格式,Date 类型可以精确表示 1970 年 1 月 1 日之前及之后 285616年的日期

要创建日期对象,就使用 new 操作符来调用 Date 构造函数:

let now = new Date(); 

在不给 Date 构造函数传参数的情况下,创建的对象将保存当前日期和时间。要基于其他日期和时 间创建日期对象,必须传入其毫秒表示(UNIX 纪元 1970 年 1 月 1 日午夜之后的毫秒数)。ECMAScript 为此提供了两个辅助方法:Date.parse()和 Date.UTC()

Date.parse()方法接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒 数。ECMA-262 第 5 版定义了 Date.parse()应该支持的日期格式,填充了第 3 版遗留的空白。所有实 现都必须支持下列日期格式:

  • “月/日/年”,如"5/23/2019";
  • “月名 日, 年”,如"May 23, 2019";
  • “周几 月名 日 年 时:分:秒 时区”,如"Tue May 23 2019 00:00:00 GMT-0700";
  • ISO 8601 扩展格式“YYYY-MM-DDTHH:mm:ss.sssZ”,如 2019-05-23T00:00:00(只适用于 兼容 ES5 的实现)。

比如,要创建一个表示“20121年 8月 17 日”的日期对象,可以使用以下代码:

let someDate = new Date(Date.parse("May 23, 2019")); 

如果传给 Date.parse()的字符串并不表示日期,则该方法会返回 NaN。如果直接把表示日期的字 符串传给 Date 构造函数,那么 Date 会在后台调用 Date.parse()。换句话说,下面这行代码跟前面 那行代码是等价的:

let someDate = new Date("May 23, 2019"); 

这两行代码得到的日期对象相同。

注意 不同的浏览器对 Date 类型的实现有很多问题。比如,很多浏览器会选择用当前日 期替代越界的日期,因此有些浏览器会将"January 32, 2019"解释为"February 1, 2019"。Opera 则会插入当前月的当前日,返回"January 当前日, 2019"。就是说,如 果是在 9 月 21 日运行代码,会返回"January 21, 2019"。

Date.UTC()方法也返回日期的毫秒表示,但使用的是跟 Date.parse()不同的信息来生成这个值。 传给 Date.UTC()的参数是零起点月数(1 月是 0,2 月是 1,以此类推)、日(131)、(023)、 分、秒和毫秒。这些参数中,只有前两个(年和月)是必需的。如果不提供日,那么默认为 1 日。其他 参数的默认值都是 0。下面是使用 Date.UTC()的两个例子:

// GMT 时间 2000 年 1 月 1 日零点
let y2k = new Date(Date.UTC(2000, 0)); 
// GMT 时间 2005 年 5 月 5 日下午 5 点 55 分 55 秒
let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55)); 
​

这个例子创建了两个日期 。第一个日期是 2000 年 1 月 1 日零点(GMT),2000 代表年,0 代表月 (1 月)。因为没有其他参数(日取 1,其他取 0),所以结果就是该月第 1 天零点。第二个日期表示 2005 年 5 月 5 日下午 5 点 55 分 55 秒(GMT)。虽然日期里面涉及的都是 5,但月数必须用 4,因为月数是零 起点的。小时也必须是 17,因为这里采用的是 24 小时制,即取值范围是 0~23。其他参数就都很直观了。

Date.parse()一样,Date.UTC()也会被 Date 构造函数隐式调用,但有一个区别:这种情况 下创建的是本地日期,不是 GMT 日期。不过 Date 构造函数跟 Date.UTC()接收的参数是一样的。因 此,如果第一个参数是数值,则构造函数假设它是日期中的年,第二个参数就是月,以此类推。前面的例子也可以这样来写:

// 起始时间
let start = Date.now(); 
// 调用函数
doSomething(); 
// 结束时间
let stop = Date.now(), 
result = stop - start;