一文搞懂JS四大特殊内置对象:Date/RegExp/Error/ArrayBuffer
Date、RegExp、Error、ArrayBuffer是JavaScript中最常用的特殊内置对象,也是深拷贝、类型检测等场景中必须特殊处理的类型。很多前端开发者只会简单使用它们,却不了解其底层原理和常见坑点,导致频繁踩坑。本文将从核心定义→底层原理→完整API→常见坑点→实际应用→深拷贝处理六个维度,带你彻底搞懂这四个对象。
一、Date:日期时间处理对象
1. 核心定义
Date是JavaScript中用于处理日期和时间 的内置对象,它本质上是一个基于Unix时间戳的整数。
2. 底层原理
Date对象内部存储的是一个64位浮点数 ,表示自1970年1月1日00:00:00 UTC(协调世界时)以来的毫秒数。
- 正数表示1970年之后的时间
- 负数表示1970年之前的时间
- 最大值约为275760年,最小值约为-271821年
3. 完整常用API
(1)创建Date对象
javascript
// 1. 创建当前时间的Date对象
const now = new Date();
// 2. 从时间戳创建(最常用,跨环境一致)
const date1 = new Date(1715606400000); // 2024-05-14 00:00:00
// 3. 从日期字符串创建(不推荐,兼容性差)
const date2 = new Date('2024-05-14');
const date3 = new Date('2024-05-14T12:00:00');
// 4. 从年月日时分秒创建(月份从0开始!)
const date4 = new Date(2024, 4, 14, 12, 0, 0); // 2024年5月14日
(2)获取时间信息
javascript
const date = new Date();
// 本地时间
date.getFullYear(); // 完整年份(2024)
date.getMonth(); // 月份(0-11,0表示1月)
date.getDate(); // 日期(1-31)
date.getDay(); // 星期(0-6,0表示周日)
date.getHours(); // 小时(0-23)
date.getMinutes(); // 分钟(0-59)
date.getSeconds(); // 秒(0-59)
date.getMilliseconds(); // 毫秒(0-999)
date.getTime(); // 时间戳(最常用)
// UTC时间(与本地时区无关)
date.getUTCFullYear();
date.getUTCMonth();
// ... 其他UTC方法类似
(3)设置时间信息
javascript
const date = new Date();
date.setFullYear(2025);
date.setMonth(11); // 12月
date.setDate(25);
date.setHours(10);
date.setMinutes(30);
date.setTime(1715606400000); // 直接设置时间戳
(4)转换为字符串
javascript
const date = new Date();
date.toString(); // "Tue May 14 2024 12:00:00 GMT+0800 (中国标准时间)"
date.toLocaleString(); // "2024/5/14 12:00:00"
date.toLocaleDateString(); // "2024/5/14"
date.toLocaleTimeString(); // "12:00:00"
date.toISOString(); // "2024-05-14T04:00:00.000Z"(UTC时间,最常用)
date.getTime().toString(); // "1715606400000"(时间戳字符串)
4. 最常见的坑点
- 月份从0开始 :这是最著名的坑,
getMonth()返回0-11,0表示1月,11表示12月 - 年份是完整年份 :不要用
getYear()(已废弃),永远用getFullYear() - 时区问题 :
toLocaleString()等方法返回本地时间,toISOString()返回UTC时间 - 日期字符串解析不一致 :不同浏览器对
new Date('2024-05-14')的解析可能不同,永远优先使用时间戳
5. 实际应用场景
- 时间格式化(推荐使用
dayjs或date-fns,不要自己写) - 倒计时功能
- 时间戳转换
- 日期比较和计算
6. 深拷贝处理
javascript
// 正确的深拷贝方式
const original = new Date();
const clone = new Date(original.getTime());
二、RegExp:正则表达式对象
1. 核心定义
RegExp(Regular Expression)是用于匹配字符串中字符组合的模式,是处理字符串最强大的工具之一。
2. 底层原理
正则表达式的执行分为两个阶段:
- 编译阶段:将正则表达式字符串编译成内部的状态机
- 执行阶段:用编译好的状态机去匹配目标字符串
3. 完整常用API
(1)创建RegExp对象
javascript
// 1. 字面量方式(推荐,编译一次)
const regex1 = /abc/g;
// 2. 构造函数方式(动态生成正则)
const regex2 = new RegExp('abc', 'g');
const pattern = 'abc';
const regex3 = new RegExp(pattern, 'i');
(2)RegExp对象方法
javascript
const regex = /hello (\w+)/g;
const str = 'hello world, hello javascript';
// test():测试是否匹配,返回布尔值
regex.test(str); // true
// exec():执行匹配,返回匹配结果数组或null
let result;
while ((result = regex.exec(str)) !== null) {
console.log(result[0]); // "hello world", "hello javascript"
console.log(result[1]); // "world", "javascript"
console.log(regex.lastIndex); // 11, 28
}
(3)字符串的正则方法
javascript
const str = 'hello world, hello javascript';
const regex = /hello (\w+)/g;
// match():返回所有匹配结果数组
str.match(regex); // ["hello world", "hello javascript"]
// matchAll():返回所有匹配结果的迭代器(ES2020)
for (const match of str.matchAll(regex)) {
console.log(match[1]); // "world", "javascript"
}
// replace():替换匹配的字符串
str.replace(regex, 'hi $1'); // "hi world, hi javascript"
// search():返回第一个匹配的索引,没有匹配返回-1
str.search(/world/); // 6
// split():用正则分割字符串
str.split(/[, ]+/); // ["hello", "world", "hello", "javascript"]
4. 最常见的坑点
-
贪婪匹配 :默认是贪婪匹配,会尽可能多的匹配字符
javascriptconst str = '<div>hello</div><div>world</div>'; str.match(/<div>(.*)<\/div>/); // ["<div>hello</div><div>world</div>", "hello</div><div>world"] str.match(/<div>(.*?)<\/div>/); // 非贪婪匹配,["<div>hello</div>", "hello"] -
lastIndex属性 :带
g修饰符的正则,lastIndex会记录上次匹配的位置,下次匹配从该位置开始 -
特殊字符转义 :
. * + ? ^ $ { } [ ] ( ) | \ /这些字符需要转义 -
性能问题:过于复杂的正则可能导致回溯爆炸,甚至浏览器崩溃
5. 实际应用场景
- 表单验证(手机号、邮箱、身份证等)
- 字符串替换和提取
- 语法高亮
- 路由匹配
6. 深拷贝处理
javascript
// 正确的深拷贝方式
const original = /abc/gi;
const clone = new RegExp(original.source, original.flags);
三、Error:错误处理对象
1. 核心定义
Error是JavaScript中表示运行时错误的基类,所有内置错误类型都继承自它。它用于在程序运行出错时,传递错误的详细信息。
2. 内置错误类型
JavaScript提供了7种内置错误类型:
| 错误类型 | 说明 |
|---|---|
SyntaxError |
语法错误,代码不符合JavaScript语法规范 |
ReferenceError |
引用错误,引用了不存在的变量 |
TypeError |
类型错误,变量的类型不符合预期 |
RangeError |
范围错误,数值超出了有效范围 |
URIError |
URI错误,encodeURI()或decodeURI()参数无效 |
EvalError |
eval()函数执行错误(已基本废弃) |
AggregateError |
聚合错误,多个错误打包成一个(ES2021) |
3. 完整常用API
(1)创建Error对象
javascript
// 1. 创建普通错误
const err = new Error('这是一个错误信息');
// 2. 创建特定类型的错误
const typeErr = new TypeError('类型错误');
const refErr = new ReferenceError('引用错误');
// 3. 自定义错误类型(ES6)
class MyError extends Error {
constructor(message, code) {
super(message);
this.name = 'MyError';
this.code = code;
// 修复原型链
Object.setPrototypeOf(this, MyError.prototype);
}
}
(2)Error对象属性
javascript
const err = new Error('这是一个错误信息');
err.name; // "Error"(错误类型名称)
err.message; // "这是一个错误信息"(错误描述)
err.stack; // 错误堆栈信息(非标准,但所有浏览器都支持)
err.cause; // 错误原因(ES2022新增)
(3)错误处理语法
javascript
// try/catch/finally
try {
// 可能出错的代码
throw new Error('出错了');
} catch (err) {
// 处理错误
console.error(err.message);
} finally {
// 无论是否出错,都会执行
console.log('执行完毕');
}
// 异步错误处理(Promise)
fetch('/api/data')
.then(res => res.json())
.catch(err => {
console.error('请求失败:', err);
});
// 异步错误处理(async/await)
async function fetchData() {
try {
const res = await fetch('/api/data');
const data = await res.json();
return data;
} catch (err) {
console.error('请求失败:', err);
}
}
4. 最常见的坑点
-
不要捕获所有错误 :不要写空的
catch块,也不要捕获Error基类而不做任何处理 -
异步错误无法被同步try/catch捕获 :
javascript// ❌ 错误:无法捕获setTimeout中的错误 try { setTimeout(() => { throw new Error('异步错误'); }, 1000); } catch (err) { console.error(err); // 永远不会执行 } -
不要忽略错误:至少要打印错误信息,方便调试
-
Error对象的stack属性是非标准的:不同浏览器的堆栈格式可能不同
5. 实际应用场景
- 程序错误处理和调试
- 自定义错误类型,区分不同的错误场景
- 异步请求错误处理
- 错误监控和上报
6. 深拷贝处理
javascript
// 正确的深拷贝方式
const original = new Error('这是一个错误');
const clone = new Error(original.message);
clone.name = original.name;
clone.stack = original.stack;
if (original.cause) {
clone.cause = original.cause;
}
四、ArrayBuffer:二进制数据缓冲区
1. 核心定义
ArrayBuffer是JavaScript中用于表示通用的、固定长度的原始二进制数据缓冲区的对象。它是所有二进制数据操作的基础。
2. 底层原理
ArrayBuffer会在内存中分配一块连续的、固定大小的字节空间,用于存储二进制数据。
- 你不能直接读写
ArrayBuffer的内容 - 必须通过视图对象 (
TypedArray或DataView)来操作缓冲区中的数据 - 视图对象不存储数据,只是提供了不同的方式来解读同一块内存
3. 完整常用API
(1)创建ArrayBuffer
javascript
// 创建一个8字节的缓冲区,初始值都是0
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength); // 8
(2)ArrayBuffer方法
javascript
// 截取缓冲区的一部分,返回新的ArrayBuffer
const buffer = new ArrayBuffer(8);
const slice = buffer.slice(2, 6); // 截取第2到第5个字节
console.log(slice.byteLength); // 4
// 转移缓冲区的所有权(ES2023),原缓冲区变为空
const transferred = buffer.transfer();
console.log(buffer.byteLength); // 0
console.log(transferred.byteLength); // 8
(3)通过TypedArray视图操作数据
TypedArray是一组特定类型的数组视图,用于操作相同类型的二进制数据:
javascript
const buffer = new ArrayBuffer(8);
// 创建一个Uint8Array视图,操作buffer中的数据
const uint8 = new Uint8Array(buffer);
uint8[0] = 0x12;
uint8[1] = 0x34;
uint8[2] = 0x56;
uint8[3] = 0x78;
console.log(uint8); // Uint8Array(8) [18, 52, 86, 120, 0, 0, 0, 0]
// 创建一个Uint32Array视图,同样操作同一个buffer
const uint32 = new Uint32Array(buffer);
console.log(uint32[0]); // 2018915346(0x78563412,小端序)
常见的TypedArray类型:
| 类型 | 大小(字节) | 说明 |
|---|---|---|
Uint8Array |
1 | 8位无符号整数 |
Int8Array |
1 | 8位有符号整数 |
Uint16Array |
2 | 16位无符号整数 |
Int16Array |
2 | 16位有符号整数 |
Uint32Array |
4 | 32位无符号整数 |
Int32Array |
4 | 32位有符号整数 |
Float32Array |
4 | 32位浮点数 |
Float64Array |
8 | 64位浮点数 |
(4)通过DataView视图操作数据
DataView是一个更灵活的视图,可以操作不同类型、不同字节序的数据:
javascript
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
// 写入不同类型的数据
view.setUint8(0, 0x12);
view.setUint16(1, 0x3456, false); // 大端序(默认)
view.setUint32(3, 0x789abcde, true); // 小端序
// 读取数据
console.log(view.getUint8(0)); // 18
console.log(view.getUint16(1)); // 13398
console.log(view.getUint32(3, true)); // 2023406872
4. 最常见的坑点
- 字节序问题:TypedArray使用系统的字节序(通常是小端序),DataView可以指定字节序
- 视图的偏移量和长度:创建视图时可以指定偏移量和长度,超出范围会报错
- ArrayBuffer是不可变的:你不能改变ArrayBuffer的大小,只能创建新的
- 不要直接操作ArrayBuffer:必须通过视图来读写数据
5. 实际应用场景
- 处理文件(File API)
- 网络请求(fetch获取二进制数据)
- WebGL图形编程
- WebSocket二进制通信
- 加密和解密
6. 深拷贝处理
javascript
// 正确的深拷贝方式
const original = new ArrayBuffer(8);
const clone = original.slice();
五、总结:四大对象深拷贝处理汇总
这四个对象都是深拷贝中必须特殊处理的类型,不能用普通的递归拷贝:
| 对象类型 | 正确的深拷贝方式 |
|---|---|
Date |
new Date(original.getTime()) |
RegExp |
new RegExp(original.source, original.flags) |
Error |
new Error(original.message) + 复制name、stack、cause |
ArrayBuffer |
original.slice() |
六、面试高频考点总结
Date对象的月份是从0开始还是从1开始?- 如何解决
Date对象的时区问题? - 正则表达式的贪婪匹配和非贪婪匹配有什么区别?
lastIndex属性的作用是什么?- JavaScript有哪些内置错误类型?
- 为什么异步错误无法被同步的
try/catch捕获? ArrayBuffer和TypedArray的区别是什么?TypedArray和DataView的区别是什么?- 为什么这四个对象不能用普通的递归方式深拷贝?
如果觉得这篇文章对你有帮助,欢迎点赞、收藏、关注,有任何问题可以在评论区留言讨论!