JavaScript 中的引用类型 Date 和 RegExp 详细介绍
本文深入解析 JavaScript 中的引用类型 Date 与 RegExp。详细阐述 Date 对象的创建方式、毫秒存储机制,以及 Date.parse() 与 Date.UTC() 在时区处理上的关键区别。同时介绍 RegExp 字面量与构造函数的差异、元字符转义规则,以及 exec() 和 test() 方法在处理捕获组、全局标记和 lastIndex 属性时的具体行为与注意事项。
理解引用类型与引用值
引用值(或对象)是特定引用类型的实例。在 ECMAScript 中,引用类型是将数据与功能封装在一起的结构体,常被误称为“类”。尽管 JavaScript 属于面向对象语言,但 ECMAScript 并不具备传统面向对象语言中的类和接口等基本结构。引用类型有时也被称为对象定义,因为它们规定了对象应包含的属性和方法。对象被视为特定引用类型的实例,通过 new 操作符后跟构造函数来创建新对象,该函数负责初始化新对象。
let now = new Date();这行代码创建了引用对象 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象。ECMAScript 提供了很多像Date这样的原生引用类型,帮助开发者实现常见的任务。函数也是一种引用类型。
Date 引用类型
ECMAScript 的 Date 类型参考了 Java 早期版本中的 java.util.Date。为此,Date 类型将日期保存为自协调世界时 (UTC) 1970年1月1日午夜(零时)至今所经过的毫秒数。使用这种存储方式,Date 类型可以精确地标识 1970年1月1日之前及之后 285616 年的日期。要创建日期对象,就是用 new 操作符来调用 Date 构造函数:
let now = new Date();
console.log(now);// Mon Jul 18 2022 15:36:02 GMT+0800 (中国标准时间)在不给 Date 构造函数传递参数的情况下,创建的对象保存的是当前的日期和时间。要基于其他日期和时间创建日期对象,必须传入其毫秒标识。ECMAScript 为此提供了两个辅助方法:Date.parse() 和 Date.UTC();
Date.parse() 方法
Date.parse() 方法接收一个表示日期和时间的字符串,尝试将这个字符串转换为表示该日期的毫秒数。ECMA-262 第 5 版定义了 Date.parse() 应该支持的日期格式。所有实现必须支持以下列日期格式:
- “月/日/年”,如“5/23/2022”
- “月名 日,年”,如“May 23,2022”;
- “周几,月名 日 年 时:分:秒”,如”Tue May 23 2019 00:00:00 GMT-0700“。
如果传给 Date.parse() 的字符串并不表示日期,则该方法返回 NaN。如果直接把表示日期的字符串传给 Date 的构造函数,那么 Date 会在后台调用 Date.parse()。换句话说,如下两行代码代表的意思一致。
let someDate = new Date(Date.parse("May 23,2022"));
let someDateTwo = new Date("May 23,2022");Date.UTC() 方法
Date.UTC() 方法也返回日期的毫秒标识,但使用的是跟 Date.parse() 不同的信息来生成这个值。传给 Date.UTC() 的参数是年,零起点月数(1月是0,2月是1,依次类推),日(1-31),时(0-23),分,秒和毫秒。这些参数中,只有(年和月)是必须的。如果不提供日,那么默认为 1 日。其他的默认值都是 0。
let y2k = new Date(Date.UTC(2000,0)); //GMT 时间2000年1月1日零点。
let allLives = new Date(Date.UTC(2005,3,5,17,55,55));// 2005年5月5日下午5点55分55秒与 Date.parse() 一样,Date.UTC() 也会被 Date 的构造函数隐式调用,但有一个区别,这种情况下创建的是本地日期,不是 GMT 日期。也就是说,隐式调用情况下,我们传递给 Date.UTC() 方法的返回的毫秒数是本地时间距离 1970年1月1日的毫秒数,我们传递的这个日期,在 JavaScript 看来是 UTC 时间,它默认已经加上了时区插值的毫秒数。但是显示的调用 UTC() 方法时,传递的日期就是 GMT 时间。GMT 时间与 UTC 时间的关系为:GMT = UTC + 0。
let utc = new Date(2022, 6, 18, 17, 15);//
console.log(utc.toString());//Mon Jul 18 2022 17:15:00 GMT+0800 (中国标准时间)
console.log(new Date(Date.UTC(2022, 6, 18, 17, 15)));//Tue Jul 19 2022 01:15:00 GMT+0800 (中国标准时间)Date.parse() 方法与 Date.UTC() 方法的使用注意:首先 parse 方法和 UTC 方法都可以隐式调用。所以我们可以这么理解,有两种方式可以创建日期。给 Date 构造函数传入的是一个日期字符串时,会默认调用 parse 方法,传入以整数的形式表示的日期会调用 UTC 方法。但是默认情况下我们传递的日期是加上了时区的毫秒数的,这个一定要清楚。如果要使用 UTC 方法的传递 GMT 时间,一定要显示的调用 UTC() 方法。当要明确的求取某个时区的某个时间的毫秒时,需要使用 parse() 方法。
继承的方法
Date 继承自 Object 方法,因此也会有 toLocaleString,toString() 和 valueOf 方法。
Date 类型重写了这三个方法的实现:
- Date 类型的 toLocaleString() 方法返回与浏览器运行的本地环境一致的时间和日期。这通常意味着格式中包含针对时间的 AM(上午)或 PM(下午),但不包含时区信息。
- toString() 返回带时区信息的日期和时间,时间也是以 24 小时制表示的。
- Date 类型的 valueOf 方法根本就不返回字符串,这个方法被重写后返回的是日期的毫秒表示。
下面是 toLocaleString,toString() 和 valueOf 的测试打印数据,可供参考。
Date(2022, 6, 14, 10, 50) toString: Thu Jul 14 2022 10:50:00 GMT+0800 (中国标准时间) toLocaleString: 2022/7/14 10:50:00 valueOf: 1657767000000
**************************** Date.UTC(2022, 6, 14, 10, 50) toString: Thu Jul 14 2022 18:50:00 GMT+0800 (中国标准时间) toLocaleString: 2022/7/14 18:50:00 valueOf: 1657795800000
**************************** Date.parse("Thur 07 14 2022 10:50:00 GMT+0800") toString: Thu Jul 14 2022 10:50:00 GMT+0800 (中国标准时间) toLocaleString: 2022/7/14 10:50:00 valueOf: 1657767000000 **************************** Date("Thur 07 14 2022 10:50:00 GMT+0800") toString: Thu Jul 14 2022 10:50:00 GMT+0800 (中国标准时间) toLocaleString: 2022/7/14 10:50:00 valueOf: 1657767000000 **************************** Date.now() toString: 1657767000086 toLocaleString: 1,657,767,000,086 valueOf: 1657767000086
RegExp
ECMAScript 通过 RegExp 类型支持正则表达式。正则表达式使用类似 Perl 的简洁语法来创建。
let expersiion = /pattern/flags这个正则表达式的 pattern(模式)可以是任何简单或复杂的正则表达式,包括字符类,限定符,分组,向前查找和反向引用。每个正则表达式可以带零个或多个 flags(标记),用于控制正则表达式的行为。
下面给出了匹配模式的标记:
- g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。
- i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。
- m:多行模式,表示查找到一行末尾时会继续查找
- y:粘贴模式,表示只查找从 lastIndex 开始及之后的字符串。
- u:Unicode 模式,启用 Unicode 匹配。
- s:doAll 模式,表示元字符,匹配任何字符(包括\n 或\r)。
使用不同模式和标记可以创建出各种正则表达式,比如:
//匹配字符串中的所有“at”
let pattern = /at/g;
//匹配第一个“bat”或“cat”,忽略大小写
let pattern2 = /[bc]at/i;
//匹配所有以“at”结尾的三字符组合,忽略大小写。
let pattern3 = /.at/gi与其他语言的正则表达式类似,所有元字符在模式中也必须转义,包括: ( [ \ ^ $ |)] ? * + . 元字符在正则表达式中都有一种或多种特殊功能,所有要匹配上面这些字符本身,就必须使用反斜杠来转义。
//匹配第一个“bat”或“cat”,忽略大小写
let pattern2 = /[bc]at/i;
//匹配第一个“[bc]at”,忽略大小写
let pattern2 = /\[bc\]at/i;
//匹配所有以“at”结尾的三字符组合,忽略大小写。
let pattern3 = /.at/gi
//匹配所有".at",忽略大小写。
let pattern3 = /\.at/gi前面例子中的正则表达式都是使用字面量形式定义的。正则表达式也可以使用 RegExp 构造函数来创建,它接收两个参数:模式字符串和(可选的)标记字符串。任何使用字面量定义的正则表达式也可以通过构造函数来创建:
//匹配第一个“bat”或“cat”,忽略大小写
let pattern1 = /[bc]at/i;
//跟pattern1一样,只不过是用构造函数创建的。
let pattern2 = new RegExp("[bc]at","i");这里的 pattern1 和 pattern2 是等效的正则表达式。注意,RegExp 构造函数的两个参数都是字符串。因为 RegExp 的模式参数是字符串,所以某些情况下需要二次转义。所有元字符都必须二次转义,包括转义字符,如\n(\转义后的字符串是\,在正则表达式中则要写成\\)。
| 字面量模式 | 对应的字符串 |
|---|---|
| /[bc]at/ | "\[bc]\]at" |
| /.at/ | "\at." |
| /name/age/ | "name\/age" |
| /\d/\d{1,2} | "\d.\d{1,2}" |
| /\w\hello\123 | "\w\\hello\\123" |
RegExp 实例方法
exec()
RegExp 的主要方法是 exec(),主要用于配合捕获组使用。这个方法只接受一个参数,即要应用模式的字符串。如果找到了匹配项,则返回包含第一个匹配信息的数组;如果没有找到匹配项,则返回 null。返回的数组虽然是 Array 的实例,但包含两个额外的属性:index 和 input。index 是字符串中匹配模式的起始位置,input 是要查找的字符串。这个数据的第一个元素是匹配整个模式的字符串,其他元素是表达式中的捕获组匹配的字符串。如果模式中没有捕获组,则数组只包含一个元素。
let text = "mom and dad and baby";
let pattern = /mom( and dad( and baby)?)?/gi
let matches = pattern.exec(text);
console.log(matches.index); //0
console.log(matches.input); //"mom and dad and baby"
console.log(matches[0]);// "mom and dad and baby"
console.log(matches[1]);// " and dad and baby"
console.log(matches[2]);// " and baby"在这个例子中,模式包含两个捕获组:最内部的匹配项“ and baby”,以及外部的匹配项“ and dad” 或“ and dad and baby”。调用 exec() 后找到了一个匹配项。因为整个字符串匹配模式,所以 matches 数组的 index 属性就是 0。数组的第一个元素是匹配的整个字符串,第二个元素是匹配第一个捕获组的字符串,第三个元素是匹配第二个捕获组的字符串。 如果模式设置了全局标记,则每次调用 exec() 方法会返回一个匹配的信息。如果没有设置全局标记,则无论对同一个字符串调用多少次 exec(),也只会返回第一个匹配的信息。
let text = "cat, bat, sat, fat";
let pattern = /.at/;
let matches = pattern.exec(text);
console.log(matches.index);//0
console.log(matches[0]);//cat
console.log(pattern.lastIndex);//0
matches = pattern.exec(text);
console.log(matches.index);//0
console.log(matches[0]);//cat
console.log(pattern.lastIndex);//0上面的例子没有设置全局标记,因此调用 exec() 只返回第一个匹配项("cat")。lastIndex 在非全局模式下始终不变。 如果在这个模式上设置了 g 标记,则每次调用 exec() 都会在字符串向前搜索下一个匹配项。
let text = "cat, bat, sat, fat";
let pattern = /.at/g;
let matches = pattern.exec(text);
console.log(matches.index);//0
console.log(matches[0]);//cat
console.log(pattern.lastIndex);//3
matches = pattern.exec(text);
console.log(matches.index);//5
console.log(matches[0]);//bat
console.log(pattern.lastIndex);//8
matches = pattern.exec(text);
console.log(matches.index);//10
console.log(matches[0]);//sat
console.log(pattern.lastIndex);//13如果设置了粘附标记 y,则每次调用 exec() 就只会在 lastIndex 的位置上寻找匹配项。
粘附标记会覆盖全局标记:
let text = "cat, bat, sat, fat";
let pattern = /.at/y;
let matches = pattern.exec(text);
console.log(matches.index);//0
console.log(matches[0]);//cat
console.log(pattern.lastIndex);//3
//以索引3对应的字符开头找不到匹配项,因此exec() 返回null
//
matches = pattern.exec(text);
console.log(matches);//null
console.log(pattern.lastIndex);//0
//向前设置lastIndex 可以让粘附的模式通过exec()找到下一个匹配项。
pattern.lastIndex = 5;
matches = pattern.exec(text);
console.log(matches.index);//5
console.log(matches[0]);//bat
console.log(pattern.lastIndex);//8test()
正则表达式的另一个方法是 test(),接收一个字符串参数。如果输入的文本与模式匹配,则参数返回 true,否则返回 false。这个方法适用于只想测试模式是否匹配,而不需要世界匹配内容的情况。
let text = "000-00-0000";
let pattern = /\d{3}-\d{2}-\d{4}/;
if(pattern.test(test)){
console.log("The pattern was matched.");
}继承的方法
无论正则表达式怎么创建的,继承的方法 toLocalString()toString() 都返回正则表达式的字面量表示。
let pattern = new RegExp("\\[bc]\\at]at","gi");
console.log(pattern.toString()); ///\[bc\]at/gi
console.log(pattern.toLocaleString()); // /\[bc\]at/gi
console.log(pattern.valueOf());///\[bc]\at]at/gi到此这篇关于 JavaScript 中的引用类型 Date 和 RegExp 的详细介绍的文章就介绍到这了,更多相关 JS 引用类型 Date 和 RegExp 内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
