首页 阅读DAY5 JavaScript高级程序设计 5章上 基本引用类型
文章
取消

阅读DAY5 JavaScript高级程序设计 5章上 基本引用类型

开始阅读JavaScript高级程序设计(第5版)学习JS,总共有1000+页,非常全面,短期看完不太现实,找到了一篇博客,花些时间跟着这篇博客过一下红宝书。

JavaScript高级程序设计(第5版)微信读书

红宝书《JavaScript高级程序设计(第5版)》学习大纲 - 大前端全栈开发 - SegmentFault 思否

引用值(或者对象)是某个特定引用类型的实例。引用类型指的是:变量存储的不是值的本身,而是值的”地址”(引用)。

在ECMAScript中,引用类型是把数据和功能组织到一起的结构,经常被人错误地称作“类”,虽然与类相似,但是实现不同。虽然从技术上讲JavaScript是一门面向对象语言,但ECMAScript缺少传统的面向对象编程语言所具备的某些基本结构,包括类和接口

引用类型有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。函数也是一种引用类型,也是对象,函数实际上是Function这个引用类型的实例。因为函数也是对象,所以函数也有方法,可以用于增强其能力。

JavaScript 中除了原始值(string、number、boolean 等)以外都是引用类型,对象、数组、函数等

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

1
let now = new Date();

这行代码创建了引用类型Date(是ES的原生引用类型之一)的一个新实例,并将它保存在变量now中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象。

Date:

Date 内部将日期存为 UTC 1970-01-01 00:00:00 至今的毫秒数,可精确表示该时间点前后约 28 万年的日期。

创建日期对象
1
let now = new Date(); // 当前时间

不传参数默认为当前时间。传入其他日期需提供毫秒数,ECMAScript 提供了两个辅助方法。


Date.parse() — 字符串转毫秒

接收日期字符串,返回对应毫秒数。支持以下格式:

格式示例
月/日/年"5/23/2019"
月名 日, 年"May 23, 2019"
完整时区格式"Tue May 23 2019 00:00:00 GMT-0700"
ISO 8601"2019-05-23T00:00:00"
1
2
3
let someDate = new Date(Date.parse("May 23, 2019"));
// 等价写法(构造函数自动调用 Date.parse)
let someDate = new Date("May 23, 2019");

字符串无法解析时返回 NaN


Date.UTC() — 参数转毫秒(UTC 时间)

参数依次为:年、月(0起点)、日、时、分、秒、毫秒,只有年和月必填,其余默认 0,日默认 1。

1
2
let y2k = new Date(Date.UTC(2000, 0)); // UTC 2000-01-01 00:00
let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55)); // UTC 2005-05-05 17:55:55

构造函数也接受相同参数,但创建的是本地时间而非 UTC:

1
2
let y2k = new Date(2000, 0); // 本地 2000-01-01 00:00
let allFives = new Date(2005, 4, 5, 17, 55, 55); // 本地 2005-05-05 17:55:55

Date.now() — 当前时间戳

返回当前时刻的毫秒数,常用于性能计时:

1
2
3
let start = Date.now();
doSomething();
let result = Date.now() - start; // 耗时毫秒数

继承的方法:

Date 类型重写了三个方法,但行为各不相同:

toLocaleString():返回符合本地环境的日期时间,含 AM/PM,不含时区。格式因浏览器而异,仅适合调试,不适合显示。

toString():返回含时区信息的日期时间,24小时制。同样因浏览器而异。

1
2
toLocaleString()  2/1/2019 12:00:00 AM
toString()  Thu Feb 1 2019 00:00:00 GMT-0800 (Pacific Standard Time)

valueOf():不返回字符串,返回日期的毫秒时间戳。因此可以直接用比较运算符对日期排序:

1
2
3
4
let date1 = new Date(2019, 0, 1); // 2019年1月1日
let date2 = new Date(2019, 1, 1); // 2019年2月1日

console.log(date1 < date2); // true ← 毫秒数更小 = 日期更早

日期格式化方法与日期/时间组件方法:

Date类型有几个专门用于格式化日期的方法,它们都会返回字符串。Date类型剩下的方法直接涉及取得或设置日期值的特定部分。此部分由于是列举,可直接查看原文。

RegExp:

ECMAScript 通过 RegExp 类型支持正则表达式,语法如下:

1
let expression = /pattern/flags;

pattern 可以是任意正则表达式,支持字符类、限定符、分组、向前查找、反向引用等。flags 匹配标记用于控制匹配行为:

标记名称作用
g全局模式匹配全部内容,而不是找到第一个就停止
i忽略大小写匹配时不区分大小写
m多行模式到达一行末尾后继续查找下一行
y黏附模式只从 lastIndex 位置开始匹配
uUnicode 模式启用 Unicode 匹配
s单行模式. 可匹配换行符
d索引模式结果包含每个捕获组的起止索引
1
2
3
4
5
6
7
8
// 匹配字符串中的所有"at"
let pattern1 = /at/g;

// 匹配第一个"bat"或"cat",忽略大小写
let pattern2 = /[bc]at/i;

// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;

正则表达式中的元字符必须转义才能匹配字面量:

1
2
3
4
5
6
7
8
9
10
11
12
13
( [ { \ ^ $ | ) ] } ? * + .

// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;

// 匹配第一个"[bc]at",忽略大小写
let pattern2 = /\[bc\]at/i;

// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;

// 匹配所有".at",忽略大小写
let pattern4 = /\.at/gi;

前面例子中的正则表达式都是使用字面量形式定义的。正则表达式也可以使用RegExp构造函数来创建,它接收两个参数:模式字符串和(可选的)标记字符串:

1
2
3
4
5
// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;

// 跟pattern1一样,只不过是用构造函数创建的
let pattern2 = new RegExp("[bc]at", "i");

这里的pattern1和pattern2是等效的正则表达式。注意,RegExp构造函数的两个参数都是字符串。因为RegExp的模式参数是字符串,所以在某些情况下需要二次转义

字面量:一层解析(正则引擎)

1
/\[bc\]at/ // 正则引擎看到 \[ → 匹配字面量 [

\ 只需要满足正则引擎的转义要求。


构造函数:两层解析(字符串 → 正则引擎)

1
new RegExp("\\[bc\\]at")

第一层:JS 字符串解析

  • "\\[" → 字符串值为 \[

第二层:正则引擎解析

  • \[ → 匹配字面量 [

所有元字符都必须二次转义,包括转义字符序列,如 \n(\ 转义后的字符串是 \\,在正则表达式字符串中则要写成 \\\)。下表展示了几个正则表达式的字面量形式,以及使用RegExp构造函数创建时对应的模式字符串。 image-20260419205711919

此外,使用RegExp也可以基于已有的正则表达式实例,并可选择性地修改它们的标记:

1
2
3
4
5
6
7
8
const re1 = /cat/g;
console.log(re1);  // "/cat/g"

const re2 = new RegExp(re1);
console.log(re2);  // "/cat/g"

const re3 = new RegExp(re1, "i");
console.log(re3);  // "/cat/i"

捕获组可以通过索引访问,不好的一点是索引没有提供它们实际包含内容的上下文:

1
2
3
4
5
6
const text = '2018-03-14';

const re = /(\d+)-(\d+)-(\d+)/;

console.log(re.exec(text)) ;
//["2018-03-14", "2018", "03", "14"]

命名捕获组支持将有效的JavaScript标识符与捕获组相关联,然后可以从结果的groups属性中取得捕获组:

1
2
3
4
5
6
const text = '2018-03-14';

const re = /(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/;

console.log (re.exec(text).groups) ;
// { year: "2018", month: "03", day: "14" }

RegExp实例属性:

RegExp 实例属性一览:

属性类型含义
global布尔是否设置了 g 标记
ignoreCase布尔是否设置了 i 标记
multiline布尔是否设置了 m 标记
unicode布尔是否设置了 u 标记
sticky布尔是否设置了 y 标记
dotAll布尔是否设置了 s 标记
hasIndices布尔是否设置了 d 标记
lastIndex整数下次搜索的起始位置,默认 0
source字符串模式的字面量形式(无前后斜杠)
flags字符串标记字符串

通过这些属性可以全面了解正则表达式的信息,不过实际开发中用得并不多,因为模式声明中包含这些信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let pattern1 = /\[bc\]at/i;

console.log(pattern1.global);      // false
console.log(pattern1.ignoreCase);  // true
console.log(pattern1.multiline);   // false
console.log(pattern1.lastIndex);   // 0
console.log(pattern1.source);      // "\[bc\]at"
console.log(pattern1.flags);       // "i"

let pattern2 = new RegExp("\\[bc\\]at", "i");

console.log(pattern2.global);      // false
console.log(pattern2.ignoreCase);  // true
console.log(pattern2.multiline);   // false
console.log(pattern2.lastIndex);   // 0
console.log(pattern2.source);      // "\[bc\]at"
console.log(pattern2.flags);       // "i"

虽然第一个模式是通过字面量创建的,第二个模式是通过RegExp构造函数创建的,但两个模式的source和flags属性是相同的。source和flags属性返回的是规范化之后可以在字面量中使用的形式。

RegExp实例方法:

exec() — 捕获组匹配

接收字符串,返回数组或 null。返回的数组有两个额外属性:

属性含义
index匹配的起始位置
input原始字符串

数组元素:[0] = 整个匹配,[1][2]… = 各捕获组。

js复制

1
2
3
4
5
6
7
8
let text = "mom and dad and baby";
let pattern = /mom( and dad( and baby)?)?/gi;
let matches = pattern.exec(text);

matches[0]; // "mom and dad and baby" ← 整个匹配
matches[1]; // " and dad and baby" ← 第1个捕获组
matches[2]; // " and baby" ← 第2个捕获组
matches.index; // 0

全局标记 g 对 exec() 的影响

1
2
3
4
5
6
7
8
9
10
// 无 g:每次调用都返回第一个匹配,lastIndex 始终为 0
let pattern = /.at/;
pattern.exec("cat, bat, sat, fat"); // "cat",lastIndex = 0
pattern.exec("cat, bat, sat, fat"); // "cat",lastIndex = 0

// 有 g:每次调用返回下一个匹配,lastIndex 自动更新
let pattern = /.at/g;
pattern.exec("cat, bat, sat, fat"); // "cat",lastIndex = 3
pattern.exec("cat, bat, sat, fat"); // "bat",lastIndex = 8
pattern.exec("cat, bat, sat, fat"); // "sat",lastIndex = 13

黏附标记 y — 只从 lastIndex 处匹配

1
2
3
4
5
6
7
8
let pattern = /.at/y;
pattern.exec("cat, bat, sat, fat"); // "cat",lastIndex = 3

pattern.exec("cat, bat, sat, fat"); // null(索引3是逗号,不匹配)
// 匹配失败时 lastIndex 重置为 0

pattern.lastIndex = 5; // 手动设置起点
pattern.exec("cat, bat, sat, fat"); // "bat",lastIndex = 8

y 覆盖 g:只从 lastIndex 位置开始匹配,不自动继续搜索。


test() — 只判断是否匹配

1
2
let pattern = /\d{3}-\d{2}-\d{4}/;
pattern.test("000-00-0000"); // true

只返回 true/false,不需要匹配内容时用,常用于输入验证


继承方法

1
2
3
4
let pattern = new RegExp("\\[bc\\]at", "gi");
pattern.toString(); // /\[bc\]at/gi ← 返回字面量形式
pattern.toLocaleString(); // /\[bc\]at/gi ← 同上
pattern.valueOf(); // 返回正则表达式本身

构造函数创建的 RegExptoString() 也返回字面量形式。

本文由作者按照 CC BY 4.0 进行授权

阅读DAY4 JavaScript高级程序设计 4章下 变量、作用域和内存

阅读DAY5 JavaScript高级程序设计 5章中 基本引用类型