《JavaScript全栈教程》07:内置对象与正则表达式——Date、RegExp、JSON全解
在 JavaScript 开发中,日期处理、字符串匹配、数据交换是每天都会遇到的高频场景。然而,Date 对象的月份从 0 开始、正则表达式的贪婪匹配、JSON 的序列化细节,这些都是新手甚至经验丰富的开发者容易踩坑的地方。本文将带你系统掌握 Date、RegExp、JSON 三大内置对象的核心用法、避坑技巧与实战场景,学完后你将能熟练处理时间日期、编写高效的正则表达式、安全地进行数据序列化与反序列化。
本篇核心收获
- 掌握 Date 对象的创建、方法使用、时区与时间戳处理,牢记月份从 0 开始的避坑要点
- 理解正则表达式的语法规则、分组提取、贪婪与非贪婪匹配、全局搜索模式
- 掌握 JSON 数据格式规范,熟练使用
JSON.stringify()与JSON.parse()进行序列化与反序列化 - 通过实战练习验证 Email 地址的正则表达式编写能力
一、标准对象与类型判断
在 JavaScript 的世界里,一切都是对象。但是某些对象还是和其他对象不太一样。为了区分对象的类型,我们使用 typeof 操作符获取对象的类型,它总是返回一个字符串:
typeof 123; // 'number'
typeof 123n; // 'bigint'
typeof NaN; // 'number'
typeof 'str'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Math.abs; // 'function'
typeof null; // 'object'
typeof []; // 'object'
typeof {}; // 'object'可见,number、bigint、string、boolean、function 和 undefined 有别于其他类型。特别注意 null 的类型是 object,Array 的类型也是 object,如果我们用 typeof 将无法区分出 null、Array 和通常意义上的 object —— {}。
1.1 包装对象
除了这些类型外,JavaScript 还提供了包装对象,熟悉 Java 的小伙伴肯定很清楚 int 和 Integer 这种暧昧关系。
number、boolean 和 string 都有包装对象。在 JavaScript 中,字符串也区分 string 类型和它的包装类型。包装对象用 new 创建:
let n = new Number(123); // 123, 生成了新的包装类型
let b = new Boolean(true); // true, 生成了新的包装类型
let s = new String('str'); // 'str', 生成了新的包装类型虽然包装对象看上去和原来的值一模一样,显示出来也是一模一样,但它们的类型已经变为 object 了!所以,包装对象和原始值用 === 比较会返回 false:
typeof new Number(123); // 'object'
new Number(123) === 123; // false
typeof new Boolean(true); // 'object'
new Boolean(true) === true; // false
typeof new String('str'); // 'object'
new String('str') === 'str'; // false所以不要使用包装对象! 尤其是针对 string 类型。
如果我们在使用 Number、Boolean 和 String 时,没有写 new 会发生什么情况?此时,Number()、Boolean() 和 String() 被当做普通函数,把任何类型的数据转换为 number、boolean 和 string 类型(注意不是其包装类型):
let n = Number('123'); // 123,相当于 parseInt() 或 parseFloat()
typeof n; // 'number'
let b = Boolean('true'); // true
typeof b; // 'boolean'
let b2 = Boolean('false'); // true! 'false' 字符串转换结果为 true!因为它是非空字符串!
let b3 = Boolean(''); // false
let s = String(123.45); // '123.45'
typeof s; // 'string'1.2 类型判断规则总结
有这么几条规则需要遵守:
- 不要使用
new Number()、new Boolean()、new String()创建包装对象 - 用
parseInt()或parseFloat()来转换任意类型到number - 用
String()来转换任意类型到string,或者直接调用某个对象的toString()方法 - 通常不必把任意类型转换为
boolean再判断,因为可以直接写if (myVar) {...} typeof操作符可以判断出number、boolean、string、function和undefined- 判断
Array要使用Array.isArray(arr) - 判断
null请使用myVar === null - 判断某个全局变量是否存在用
typeof window.myVar === 'undefined' - 函数内部判断某个变量是否存在用
typeof myVar === 'undefined'
避坑指南:任何对象都有 toString() 方法吗?null 和 undefined 就没有!这两个特殊值要除外,虽然 null 还伪装成了 object 类型。
更细心的同学指出,number 对象调用 toString() 报 SyntaxError:
123.toString(); // SyntaxError遇到这种情况,要特殊处理一下:
123..toString(); // '123', 注意是两个点!
(123).toString(); // '123'模块小结:JavaScript 的类型系统较为灵活,
typeof对于null和Array都会返回object,需要结合其他方式精确判断。包装对象会产生object类型,与原始值的===比较会失败,应避免使用。
二、Date 对象
在 JavaScript 中,Date 对象用来表示日期和时间。
2.1 获取系统当前时间
要获取系统当前时间,用:
let now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015,年份
now.getMonth(); // 5,月份,注意月份范围是 0~11,5 表示六月
now.getDate(); // 24,表示 24 号
now.getDay(); // 3,表示星期三
now.getHours(); // 19,24 小时制
now.getMinutes(); // 49,分钟
now.getSeconds(); // 22,秒
now.getMilliseconds(); // 875,毫秒数
now.getTime(); // 1435146562875,以 number 形式表示的时间戳注意:当前时间是浏览器从本机操作系统获取的时间,所以不一定准确,因为用户可以把当前时间设定为任何值。
2.2 创建指定日期和时间
如果要创建一个指定日期和时间的 Date 对象,可以用:
let d = new Date(2015, 5, 19, 20, 15, 30, 123);
console.log(d); // Fri Jun 19 2015 20:15:30 GMT+0800 (CST)特别坑爹的地方:JavaScript 的月份范围用整数表示是 0~11,0 表示一月,1 表示二月……,所以要表示 6 月,我们传入的是 5!
特别注意:JavaScript 的 DateTime 对象月份值从 0 开始,牢记
0 = 1月,1 = 2月,2 = 3月,……,11 = 12月。
第二种创建一个指定日期和时间的方法是解析一个符合 ISO 8601 格式的字符串:
let d = Date.parse('2015-06-24T19:49:22.875+08:00');
console.log(d); // 1435146562875但它返回的不是 Date 对象,而是一个时间戳。不过有时间戳就可以很容易地把它转换为一个 Date 对象:
let d = new Date(1435146562875);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
d.getMonth(); // 5注意:使用 Date.parse() 时传入的字符串使用实际月份 01-12,转换为 Date 对象后 getMonth() 获取的月份值为 0-11。
2.3 时区
Date 对象表示的时间总是按浏览器所在时区显示的,不过我们既可以显示本地时间,也可以显示调整后的 UTC 时间:
let d = new Date(1435146562875);
d.toLocaleString(); // '2015/6/24 下午7:49:22',本地时间(北京时区+8:00),显示的字符串与操作系统设定的格式有关
d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT', UTC 时间,与本地时间相差 8 小时那么在 JavaScript 中如何进行时区转换呢?实际上,只要我们传递的是一个 number 类型的时间戳,我们就不用关心时区转换。任何浏览器都可以把一个时间戳正确转换为本地时间。
时间戳是什么?时间戳是一个自增的整数,它表示从 1970 年 1 月 1 日零时整的 GMT 时区开始的那一刻,到现在的毫秒数。假设浏览器所在电脑的时间是准确的,那么世界上无论哪个时区的电脑,它们此刻产生的时间戳数字都是一样的,所以,时间戳可以精确地表示一个时刻,并且与时区无关。
所以,我们只需要传递时间戳,或者把时间戳从数据库里读出来,再让 JavaScript 自动转换为当地时间就可以了。
要获取当前时间戳,可以用:
// 方法1:
console.log(Date.now());
// 方法2:
console.log(new Date().getTime());2.4 练习:情人节留言 Bug 分析
小明为了和女友庆祝情人节,特意制作了网页,并提前预定了法式餐厅。小明打算用 JavaScript 给女友一个惊喜留言:
let today = new Date();
if (today.getMonth() === 2 && today.getDate() === 14) {
alert('亲爱的,我预定了晚餐,晚上6点在餐厅见!');
}结果女友并未出现。小明非常郁闷,请你帮忙分析他的 JavaScript 代码有何问题。
问题分析:JavaScript 的月份是从 0 开始的,0 表示 1 月,1 表示 2 月,以此类推,11 表示 12 月。因此,2 月应该用 getMonth() === 1 来判断。
修复后的代码:
let today = new Date();
if (today.getMonth() === 1 && today.getDate() === 14) {
alert('亲爱的,我预定了晚餐,晚上6点在餐厅见!');
}模块小结:Date 对象的核心坑点是月份从 0 开始,创建日期时务必注意。时间戳是跨时区的可靠表示方式,推荐使用
Date.now()获取当前时间戳。
三、RegExp 正则表达式
字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。比如判断一个字符串是否是合法的 Email 地址,虽然可以编程提取 @ 前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用。
正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
3.1 正则表达式基础语法
在正则表达式中,如果直接给出字符,就是精确匹配:
| 模式 | 说明 | 示例匹配 | 示例不匹配 |
|---|---|---|---|
\d | 匹配一个数字 | '007' 中的 '0' | '00A' |
\w | 匹配一个字母或数字 | 'js' 中的 'j' | 'j-s' |
. | 匹配任意字符 | 'jsp'、'jss'、'js!' | - |
\s | 匹配空格或 Tab 等空白符 | ' '、'\t' | - |
变长字符匹配:
| 量词 | 含义 |
|---|---|
* | 任意个字符(包括 0 个, >= 0) |
+ | 至少一个字符(>= 1) |
? | 0 个或 1 个字符(0 或 1) |
{n} | n 个字符(n 个) |
{n,m} | n~m 个字符(n~m 个) |
来看一个复杂的例子:\d{3}\s+\d{3,8}。
我们来从左到右解读一下:
\d{3}表示匹配 3 个数字,例如'010'\s可以匹配一个空格(也包括 Tab 等空白符),所以\s+表示至少有一个空格,例如匹配' ','\t\t'等\d{3,8}表示 3-8 个数字,例如'1234567'
综合起来,上面的正则表达式可以匹配以任意个空格隔开的带区号的电话号码。
如果要匹配 '010-12345' 这样的号码呢?由于 - 是特殊字符,在正则表达式中,要用 \ 转义,所以,上面的正则是 \d{3}\-\d{3,8}。
但是,仍然无法匹配 '010 - 12345',因为带有空格。所以我们需要更复杂的匹配方式。
3.2 进阶语法
要做更精确地匹配,可以用 [] 表示范围:
| 模式 | 说明 |
|---|---|
[0-9a-zA-Z_] | 匹配一个数字、字母或者下划线 |
[0-9a-zA-Z_]+ | 匹配至少由一个数字、字母或者下划线组成的字符串 |
[a-zA-Z_$][0-9a-zA-Z_$]* | 匹配 JavaScript 允许的变量名(字母/下划线/$ 开头) |
[a-zA-Z_$][0-9a-zA-Z_$]{0,19} | 限制变量名长度 1-20 个字符 |
其他语法:
A|B:可以匹配 A 或 B,所以(J|j)ava(S|s)cript可以匹配'JavaScript'、'Javascript'、'javascript'或者'javascript'^:表示行的开头,^\d表示必须以数字开头$:表示行的结束,\d$表示必须以数字结束
你可能注意到了,js 也可以匹配 'jsp',但是加上 ^js$ 就变成了整行匹配,就只能匹配 'js' 了。
3.3 在 JavaScript 中使用 RegExp
JavaScript 有两种方式创建一个正则表达式:
第一种方式是直接通过 /正则表达式/ 写出来,第二种方式是通过 new RegExp('正则表达式') 创建一个 RegExp 对象。
两种写法是一样的:
let re1 = /ABC\-001/;
let re2 = new RegExp('ABC\\-001');
re1; // /ABC\-001/
re2; // /ABC\-001/注意:如果使用第二种写法,因为字符串的转义问题,字符串的两个 \ 实际上是一个 \。
判断正则表达式是否匹配,使用 test() 方法:
let re = /^\d{3}\-\d{3,8}$/;
re.test('010-12345'); // true
re.test('010-1234x'); // false
re.test('010 12345'); // false3.4 切分字符串
用正则表达式切分字符串比用固定的字符更灵活:
'a b c'.split(' '); // ['a', 'b', '', '', 'c']无法识别连续的空格,用正则表达式试试:
'a b c'.split(/\s+/); // ['a', 'b', 'c']无论多少个空格都可以正常分割。加入 , 试试:
'a,b, c d'.split(/[\s\,]+/); // ['a', 'b', 'c', 'd']再加入 ; 试试:
'a,b; c d'.split(/[\s\,\;]+/); // ['a', 'b', 'c', 'd']实战建议:如果用户输入了一组标签,记得用正则表达式来把不规范的输入转化成正确的数组。
3.5 分组提取
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用 () 表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$ 分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
let re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null如果正则表达式中定义了组,就可以在 RegExp 对象上用 exec() 方法提取出子串来。
exec() 方法在匹配成功后,会返回一个 Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串。exec() 方法在匹配失败时返回 null。
提取子串非常有用。来看一个更凶残的例子(识别合法时间):
let re = /^(0[0-9]|1[0-9]|2[0-3]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$/;
re.exec('19:05:30'); // ['19:05:30', '19', '05', '30']这个正则表达式可以直接识别合法的时间。但是有些时候,用正则表达式也无法做到完全验证,比如识别日期:
let re = /^(0[1-9]|1[0-2]|[0-9])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$/;对于 '2-30','4-31' 这样的非法日期,用正则还是识别不了,或者说写出来非常困难,这时就需要程序配合识别了。
3.6 贪婪匹配与非贪婪匹配
需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的 0:
let re = /^(\d+)(0*)$/;
re.exec('102300'); // ['102300', '102300', '']由于 \d+ 采用贪婪匹配,直接把后面的 0 全部匹配了,结果 0* 只能匹配空字符串了。
必须让 \d+ 采用非贪婪匹配(也就是尽可能少匹配),才能把后面的 0 匹配出来,加个 ? 就可以让 \d+ 采用非贪婪匹配:
let re = /^(\d+?)(0*)$/;
re.exec('102300'); // ['102300', '1023', '00']3.7 全局搜索
JavaScript 的正则表达式还有几个特殊的标志,最常用的是 g,表示全局匹配:
let r1 = /test/g;
// 等价于:
let r2 = new RegExp('test', 'g');全局匹配可以多次执行 exec() 方法来搜索一个匹配的字符串。当我们指定 g 标志后,每次运行 exec(),正则表达式本身会更新 lastIndex 属性,表示上次匹配到最后索引:
let s = 'JavaScript, VBScript, JScript and ECMAScript';
let re = /[a-zA-Z]+Script/g;
// 使用全局匹配:
re.exec(s); // ['JavaScript']
re.lastIndex; // 10
re.exec(s); // ['VBScript']
re.lastIndex; // 20
re.exec(s); // ['JScript']
re.lastIndex; // 29
re.exec(s); // ['ECMAScript']
re.lastIndex; // 44
re.exec(s); // null,直到结束仍没有匹配到注意:全局匹配类似搜索,因此不能使用 /^...$/,那样只会最多匹配一次。
正则表达式还可以指定 i 标志,表示忽略大小写,m 标志,表示执行多行匹配。
3.8 练习:验证 Email 地址
版本一:验证类似格式的 Email
let re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// 测试:
let i, success = true,
should_pass = ['someone@gmail.com', 'bill.gates@microsoft.com', 'tom@voyager.org', 'bob2015@163.com'],
should_fail = ['test#gmail.com', 'bill@microsoft', 'bill%gates@ms.com', '@voyager.org'];
for (i = 0; i < should_pass.length; i++) {
if (!re.test(should_pass[i])) {
console.log('测试失败:' + should_pass[i]);
success = false;
break;
}
}
for (i = 0; i < should_fail.length; i++) {
if (re.test(should_fail[i])) {
console.log('测试失败:' + should_fail[i]);
success = false;
break;
}
}
if (success) {
console.log('测试通过!');
}版本二:验证并提取带名字的 Email 地址
let re = /^<([a-zA-Z\s]+)>\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/;
// 测试:
let r = re.exec('<Tom Paris> <tom@voyager.org>');
if (r == null || r.toString() != ['<Tom Paris> tom@voyager.org', 'Tom Paris', 'tom@voyager.org'].toString()) {
console.log('测试失败!');
} else {
console.log('测试成功!');
}模块小结:正则表达式是字符串处理的利器,核心掌握普通字符与元字符的匹配规则、分组提取、贪婪/非贪婪匹配的区别,以及
g标志下的全局搜索行为。
四、JSON
JSON 是 JavaScript Object Notation 的缩写,它是一种数据交换格式。
在 JSON 出现之前,大家一直用 XML 来传递数据。因为 XML 是一种纯文本格式,所以它适合在网络上交换数据。XML 本身不算复杂,但是,加上 DTD、XSD、XPath、XSLT 等一大堆复杂的规范以后,任何正常的软件开发人员碰到 XML 都会感觉头大了,最后大家发现,即使你努力钻研几个月,也未必搞得清楚 XML 的规范。
终于,在 2002 年的一天,道格拉斯·克罗克福特(Douglas Crockford)同学为了拯救深陷水深火热同时又被某几个巨型软件企业长期愚弄的软件工程师,发明了 JSON 这种超轻量级的数据交换格式。
4.1 JSON 数据类型
道格拉斯同学长期担任雅虎的高级架构师,自然钟情于 JavaScript。他设计的 JSON 实际上是 JavaScript 的一个子集。在 JSON 中,一共就这么几种数据类型:
| JSON 类型 | 对应 JavaScript 类型 | 格式示例 |
|---|---|---|
number | number | 123, 3.14 |
boolean | boolean | true, false |
string | string | "hello" |
null | null | null |
array | Array | [1, 2, 3] |
object | Object | {"key": "value"} |
以及上面的任意组合。
并且,JSON 还定死了字符集必须是 UTF-8,表示多语言就没有问题了。为了统一解析,JSON 的字符串规定必须用双引号 ",Object 的键也必须用双引号 "。
由于 JSON 非常简单,很快就风靡 Web 世界,并且成为 ECMA 标准。几乎所有编程语言都有解析 JSON 的库,而在 JavaScript 中,我们可以直接使用 JSON,因为 JavaScript 内置了 JSON 的解析。
把任何 JavaScript 对象变成 JSON,就是把这个对象序列化成一个 JSON 格式的字符串,这样才能够通过网络传递给其他计算机。
如果我们收到一个 JSON 格式的字符串,只需要把它反序列化成一个 JavaScript 对象,就可以在 JavaScript 中直接使用这个对象了。
4.2 序列化:JSON.stringify()
让我们先把小明这个对象序列化成 JSON 格式的字符串:
let xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};
let s = JSON.stringify(xiaoming);
console.log(s);要输出得好看一些,可以加上参数,按缩进输出:
JSON.stringify(xiaoming, null, ' ');结果:
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" Middle School",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}第二个参数用于控制如何筛选对象的键值,如果我们只想输出指定的属性,可以传入 Array:
JSON.stringify(xiaoming, ['name', 'skills'], ' ');结果:
{
"name": "小明",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}还可以传入一个函数,这样对象的每个键值对都会被函数先处理:
function convert(key, value) {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
}上面的代码把所有属性值都变成大写:
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" MIDDLE SCHOOL",
"skills": [
"JAVASCRIPT",
"JAVA",
"PYTHON",
"LISP"
]
}自定义序列化:如果我们还想要精确控制如何序列化小明,可以给 xiaoming 定义一个 toJSON() 的方法,直接返回 JSON 应该序列化的数据:
let xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp'],
toJSON: function () {
return {
Name: this.name,
Age: this.age
};
}
};
JSON.stringify(xiaoming); // {"Name":"小明","Age":14}4.3 反序列化:JSON.parse()
拿到一个 JSON 格式的字符串,我们直接用 JSON.parse() 把它变成一个 JavaScript 对象:
JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
JSON.parse('true'); // true
JSON.parse('123.45'); // 123.45JSON.parse() 还可以接收一个函数,用来转换解析出的属性:
let obj = JSON.parse('{"name":"小明","age":14}', function (key, value) {
if (key === 'name') {
return value + '同学';
}
return value;
});
console.log(JSON.stringify(obj)); // {"name":"小明同学","age":14}4.4 练习:调用天气 API
用浏览器访问 OpenWeatherMap 的天气 API,查看返回的 JSON 数据,然后返回城市、天气预报等信息:
let url = 'https://api.openweathermap.org/data/2.5/forecast?q=Beijing,cn&appid=800f49846586c3ba6e7052cfc89af16c';
fetch(url).then(resp => {
resp.json().then(data => {
let info = {
city: data.city.name,
weather: data.list[0].weather[0].main,
time: data.list[0].dt_txt
};
alert(JSON.stringify(info, null, ' '));
});
});模块小结:JSON 是轻量级的数据交换格式,本质是 JavaScript 的子集。
JSON.stringify()用于序列化,JSON.parse()用于反序列化。可通过第二参数进行筛选或转换,也可通过toJSON()方法自定义序列化行为。
本篇核心知识点速记
类型判断与包装对象
typeof null返回'object',typeof []也返回'object',无法区分- 判断 Array 用
Array.isArray(arr),判断 null 用myVar === null - 不要使用
new Number()、new Boolean()、new String()创建包装对象 - 字符串转数字用
parseInt()/parseFloat(),转字符串用String()
Date 对象核心
- 月份从 0 开始:0=1月,1=2月,...,11=12月(最容易踩坑)
Date.parse()返回时间戳,需要new Date(时间戳)转换为 Date 对象- 时间戳表示从 1970-01-01 00:00:00 GMT 到现在的毫秒数,与时区无关
- 获取当前时间戳:
Date.now()或new Date().getTime()
正则表达式核心
\d(数字)、\w(字母/数字)、\s(空白符)、.(任意字符)- 量词:
*(0个或多个)、+(1个或多个)、?(0个或1个)、{n}、{n,m} ()用于分组提取,exec()返回匹配数组- 默认贪婪匹配(尽可能多),加
?变为非贪婪匹配 g标志表示全局匹配,会更新lastIndex属性
JSON 核心
- JSON 是 JavaScript 子集,键和字符串必须用双引号
JSON.stringify(obj, replacer, space)序列化JSON.parse(str, reviver)反序列化- 可通过
toJSON()方法自定义序列化返回值
