JavaScript V8 引擎内置对象和方法
简介
V8 是 Google 开发的 JavaScript 引擎,被 Chrome 和 Node.js 使用。V8 实现了 ECMAScript 标准中定义的所有内置对象和方法。
注意:这些内置对象属于 JavaScript 语言标准,不是 V8 特有的。只是通过 V8 引擎来实现。
已在前文详细介绍的对象
以下对象在之前的文档中已详细介绍,本文仅提供速查引用:
| 对象 | 详见文档 | 说明 |
|---|---|---|
String |
js-data-types.md |
字符串,详见"字符串"章节 |
Array |
js-data-types.md |
数组,详见"数组"章节 |
Object |
js-data-types.md |
对象,详见"对象"章节 |
Function |
js-function.md |
函数,全文介绍 |
Error 及子类 |
js-exception.md |
错误对象,全文介绍 |
全局对象(Global Object)
全局对象在全局作用域中可直接访问。在浏览器中是 window,在 Node.js 中是 global。
全局属性
console.log(NaN); // NaN(不是数字)
console.log(Infinity); // Infinity(无穷大)
console.log(undefined); // undefined
// ES2020:全局 this(统一浏览器和 Node.js)
console.log(globalThis); // 浏览器: window, Node.js: global
全局函数
eval() — 执行字符串中的 JavaScript 代码
// 不推荐使用!存在安全和性能问题
let x = 10;
eval('x = 20; console.log(x);'); // 20
// 严格模式下,eval 有自己的作用域
(function() {
'use strict';
let y = 10;
eval('let y = 30; console.log(y);'); // 30(局部作用域)
console.log(y); // 10(未被影响)
})();
parseInt() — 解析字符串为整数
console.log(parseInt('123')); // 123
console.log(parseInt('123.45')); // 123(小数部分被截断)
console.log(parseInt('abc')); // NaN
console.log(parseInt('123abc')); // 123(从开头解析,遇到非数字停止)
console.log(parseInt('0xFF')); // 255(支持十六进制)
console.log(parseInt('1010', 2)); // 10(二进制解析)
console.log(parseInt('777', 8)); // 511(八进制解析)
console.log(parseInt('FF', 16)); // 255(十六进制解析)
parseFloat() — 解析字符串为浮点数
console.log(parseFloat('3.14')); // 3.14
console.log(parseFloat('123.45abc')); // 123.45
console.log(parseFloat('abc')); // NaN
isNaN() — 判断是否为 NaN
console.log(isNaN(NaN)); // true
console.log(isNaN(123)); // false
console.log(isNaN('abc')); // true(字符串 'abc' 转为数字为 NaN)
// 注意:isNaN 会先尝试将参数转为数字
console.log(isNaN('123')); // false('123' 转为 123)
console.log(isNaN(null)); // false(null 转为 0)
console.log(isNaN(undefined)); // true
// 更好的方式:使用 Number.isNaN()(ES6)
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('abc')); // false(不会类型转换!)
isFinite() — 判断是否为有限数字
console.log(isFinite(123)); // true
console.log(isFinite(Infinity)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite(NaN)); // false
console.log(isFinite('123')); // true(类型转换)
URI 编码函数
// encodeURI() / decodeURI() — 编码/解码整个 URI
let uri = 'https://example.com/搜索?q=hello world';
let encoded = encodeURI(uri);
console.log(encoded); // https://example.com/%E6%90%9C%E7%B4%A2?q=hello%20world
console.log(decodeURI(encoded)); // 原 URI
// encodeURIComponent() / decodeURIComponent() — 编码/解码 URI 组件
let param = 'hello world & more';
console.log(encodeURIComponent(param)); // hello%20world%20%26%20more
console.log(decodeURIComponent('hello%20world')); // hello world
// 区别:encodeURI 不会编码 ? = & 等 URI 特殊字符;encodeURIComponent 会全部编码
escape() / unescape() — 已废弃,不推荐使用
// 不推荐使用!仅作了解
// console.log(escape('hello world')); // hello%20world
定时器函数(运行时提供)
注意:
setTimeout、setInterval等定时器函数不是 ECMAScript 标准规定的,而是由运行时环境(浏览器/Node.js)提供的全局函数。但它们在开发中极为常用。
setTimeout — 延迟调用(一次性)
在指定延迟后执行一次回调函数。
// 基本语法
let timerId = setTimeout(() => {
console.log('1 秒后执行');
}, 1000); // 延迟 1000 毫秒(1 秒)
// 带参数的回调
setTimeout((name) => {
console.log('Hello, ' + name);
}, 500, 'Alice'); // 500ms 后输出:Hello, Alice
// 取消定时器
let timer = setTimeout(() => {
console.log('这行不会执行');
}, 2000);
clearTimeout(timer); // 取消定时
setInterval — 循环调用(重复执行)
每隔指定时间重复执行回调函数。
// 基本语法:每 500ms 执行一次
let counter = 0;
let timer = setInterval(() => {
counter++;
console.log('计数:', counter);
if (counter >= 3) {
clearInterval(timer); // 停止定时器
console.log('定时器已停止');
}
}, 500);
// 输出:
// 计数: 1(0.5秒后)
// 计数: 2(1秒后)
// 计数: 3(1.5秒后)
// 定时器已停止
setTimeout vs setInterval 对比
// setInterval 的问题:代码执行时间可能超过间隔时间
// 假设每次执行需要 400ms,间隔设为 500ms:
// 执行 400ms → 等待 100ms → 下次执行(看起来正常)
// 但如果执行时间超过间隔,会"堆积"回调
// 替代方案:使用嵌套的 setTimeout(更精确)
let count = 0;
function tick() {
count++;
console.log('计数:', count);
if (count < 3) {
setTimeout(tick, 500); // 执行完再安排下次
}
}
setTimeout(tick, 500);
this 指向问题
// 在浏览器中,setTimeout/setInterval 的回调中 this 指向 window(非严格模式)
// 在 Node.js 中,this 指向 setTimeout 的上下文(通常是全局)
let obj = {
name: 'Alice',
greet() {
setTimeout(function() {
console.log(this); // 浏览器: window,Node.js: global 或 undefined(严格模式)
}, 100);
},
greetArrow() {
setTimeout(() => {
console.log(this.name); // 'Alice'(箭头函数继承外层 this)
}, 100);
}
};
obj.greet(); // this 不是 obj
obj.greetArrow(); // this 是 obj(箭头函数)
最小延迟和时间精度
// 浏览器中,最小延迟实际受限制(特别是后台标签页)
// HTML5 规范:嵌套的 setTimeout 延迟至少 4ms
// 但是,现代浏览器对前台标签页通常更精确
// Node.js 中
setTimeout(() => console.log('1ms 后'), 1); // 可能稍长于 1ms
setTimeout(() => console.log('0ms 后'), 0); // 实际上有最小延迟(约 1-4ms)
setImmediate(Node.js 特有)
Node.js 提供的定时器,在当前事件循环迭代结束后执行。
// 仅在 Node.js 中可用
// setImmediate(() => console.log('立即执行(当前轮次结束后)'));
// setTimeout vs setImmediate 顺序不确定
// setTimeout(() => console.log('timeout'), 0);
// setImmediate(() => console.log('immediate'));
// 两者顺序可能因情况而异
requestAnimationFrame(浏览器特有)
浏览器提供的定时器,在下次页面重绘前执行,适合动画。
// 仅在浏览器中可用
// let start = null;
// function step(timestamp) {
// if (!start) start = timestamp;
// let progress = timestamp - start;
// console.log('动画进度:', progress, 'ms');
// if (progress < 2000) {
// requestAnimationFrame(step);
// }
// }
// requestAnimationFrame(step);
queueMicrotask(ES2020)
将微任务排入队列,在当前任务完成后、下一个宏任务之前执行。
console.log('1 - 同步代码');
setTimeout(() => console.log('4 - setTimeout(宏任务)'), 0);
queueMicrotask(() => console.log('3 - queueMicrotask(微任务)'));
Promise.resolve().then(() => console.log('2 - Promise(微任务)'));
console.log('5 - 同步代码');
// 输出顺序:
// 1 - 同步代码
// 5 - 同步代码
// 2 - Promise(微任务)
// 3 - queueMicrotask(微任务)
// 4 - setTimeout(宏任务)
定时器速查表
| 函数 | 说明 | 取消函数 | 环境 |
|---|---|---|---|
setTimeout(fn, delay) |
延迟执行一次 | clearTimeout(id) |
浏览器 + Node.js |
setInterval(fn, delay) |
每隔 delay 执行 | clearInterval(id) |
浏览器 + Node.js |
setImmediate(fn) |
当前轮次结束后执行 | clearImmediate(id) |
Node.js 仅 |
requestAnimationFrame(fn) |
下次重绘前执行 | cancelAnimationFrame(id) |
浏览器仅 |
queueMicrotask(fn) |
排队微任务(ES2020) | 不可取消 | 浏览器 + Node.js |
综合示例
// 示例 1:防抖(debounce)实现
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
let debouncedInput = debounce((value) => {
console.log('搜索:', value);
}, 500);
debouncedInput('hello');
debouncedInput('hello w');
debouncedInput('hello world'); // 只有最后一次会在 500ms 后执行
// 示例 2:节流(throttle)实现
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 示例 3:倒计时
function countdown(seconds) {
let remaining = seconds;
let timer = setInterval(() => {
console.log('剩余:', remaining, '秒');
remaining--;
if (remaining < 0) {
clearInterval(timer);
console.log('倒计时结束!');
}
}, 1000);
}
// countdown(5); // 每秒输出一次,5秒后结束
// 示例 4:延迟重试
function retryWithDelay(fn, retries, delay) {
return new Promise((resolve, reject) => {
function attempt(remaining) {
fn()
.then(resolve)
.catch(error => {
if (remaining === 0) {
reject(error);
} else {
console.log(`重试剩余 ${remaining} 次...`);
setTimeout(() => attempt(remaining - 1), delay);
}
});
}
attempt(retries);
});
}
Math 对象
Math 是一个内置对象,包含数学常数和函数。不能实例化,直接调用其静态方法和属性。
数学常数
console.log(Math.E); // 2.718281828459045(自然对数底)
console.log(Math.PI); // 3.141592653589793(圆周率)
console.log(Math.LN2); // 0.6931471805599453(2 的自然对数)
console.log(Math.LN10); // 2.302585092994046(10 的自然对数)
console.log(Math.LOG2E); // 1.4426950408889634(以 2 为底 E 的对数)
console.log(Math.LOG10E); // 0.4342944819032518(以 10 为底 E 的对数)
console.log(Math.SQRT1_2); // 0.7071067811865476(1/2 的平方根)
console.log(Math.SQRT2); // 1.4142135623730951(2 的平方根)
四舍五入
// Math.floor() — 向下取整
console.log(Math.floor(3.9)); // 3
console.log(Math.floor(-3.1)); // -4
// Math.ceil() — 向上取整
console.log(Math.ceil(3.1)); // 4
console.log(Math.ceil(-3.9)); // -3
// Math.round() — 四舍五入
console.log(Math.round(3.4)); // 3
console.log(Math.round(3.5)); // 4
console.log(Math.round(-3.5)); // -3(注意:.5 时向大数方向舍入)
// Math.trunc() — 截断小数部分(ES6)
console.log(Math.trunc(3.9)); // 3
console.log(Math.trunc(-3.9)); // -3
最大值、最小值
console.log(Math.max(1, 3, 2, 5, 4)); // 5
console.log(Math.min(1, 3, 2, 5, 4)); // 1
// 结合 spread 运算符用于数组
let numbers = [3, 1, 4, 1, 5, 9];
console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1
随机数
// Math.random() — 返回 [0, 1) 之间的随机数
console.log(Math.random());
// 生成指定范围的随机整数:[min, max]
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(randomInt(1, 10)); // 1-10 之间的随机整数
// 生成指定范围的随机浮点数:[min, max)
function randomFloat(min, max) {
return Math.random() * (max - min) + min;
}
console.log(randomFloat(1.5, 5.5));
幂和平方根
console.log(Math.pow(2, 3)); // 8(2 的 3 次方,等价于 2 ** 3)
console.log(Math.sqrt(16)); // 4(平方根)
console.log(Math.cbrt(27)); // 3(立方根,ES6)
console.log(Math.hypot(3, 4)); // 5(勾股定理:√(3²+4²),ES6)
绝对值和符号
console.log(Math.abs(-5)); // 5(绝对值)
console.log(Math.sign(-5)); // -1(符号:-1 负数,1 正数,0 为零)
console.log(Math.sign(5)); // 1
console.log(Math.sign(0)); // 0
对数和指数
console.log(Math.exp(1)); // 2.718281828459045(e 的 1 次方)
console.log(Math.log(Math.E)); // 1(自然对数)
console.log(Math.log10(100)); // 2(以 10 为底)
console.log(Math.log2(8)); // 3(以 2 为底)
Math 方法速查表
| 方法 | 说明 |
|---|---|
Math.floor(x) |
向下取整 |
Math.ceil(x) |
向上取整 |
Math.round(x) |
四舍五入 |
Math.trunc(x) |
截断小数 |
Math.max(...nums) |
最大值 |
Math.min(...nums) |
最小值 |
Math.random() |
[0,1) 随机数 |
Math.pow(x,y) |
x 的 y 次方 |
Math.sqrt(x) |
平方根 |
Math.abs(x) |
绝对值 |
Math.sign(x) |
符号函数 |
Math.log(x) |
自然对数 |
Math.sin(x) / Math.cos(x) / Math.tan(x) |
三角函数 |
Math.asin(x) / Math.acos(x) / Math.atan(x) |
反三角函数 |
Date 对象
Date 用于处理日期和时间。
创建 Date 对象
// 当前时间
let now = new Date();
console.log(now); // 当前日期时间字符串
// 指定时间
let date1 = new Date('2024-01-15'); // 字符串
let date2 = new Date(2024, 0, 15); // 年, 月(0-11), 日
let date3 = new Date(2024, 0, 15, 10, 30, 0); // 年,月,日,时,分,秒
let date4 = new Date(1705315200000); // 时间戳(毫秒)
// 注意:月份是从 0 开始的!0=一月,11=十二月
console.log(new Date(2024, 0, 1).getMonth()); // 0(一月)
获取日期部分
let date = new Date('2024-05-15T10:30:45');
console.log(date.getFullYear()); // 2024(年)
console.log(date.getMonth()); // 4(月,0-11,所以是五月)
console.log(date.getDate()); // 15(日,1-31)
console.log(date.getDay()); // 3(星期几,0=周日,1=周一...)
console.log(date.getHours()); // 10(时,0-23)
console.log(date.getMinutes()); // 30(分,0-59)
console.log(date.getSeconds()); // 45(秒,0-59)
console.log(date.getMilliseconds()); // 0(毫秒,0-999)
// 获取时间戳(自 1970-01-01 00:00:00 UTC 以来的毫秒数)
console.log(date.getTime()); // 1715736645000
console.log(Date.now()); // 当前时间戳(静态方法)
设置日期部分
let date = new Date();
date.setFullYear(2025);
date.setMonth(11); // 12月(0-11)
date.setDate(25);
date.setHours(12);
date.setMinutes(0);
date.setSeconds(0);
console.log(date);
格式化输出
let date = new Date('2024-05-15');
// 转为字符串
console.log(date.toString()); // "Wed May 15 2024 ..."
console.log(date.toDateString()); // "Wed May 15 2024"
console.log(date.toTimeString()); // "00:00:00 GMT+..."
console.log(date.toISOString()); // "2024-05-15T00:00:00.000Z"
// 本地化格式
console.log(date.toLocaleDateString('zh-CN')); // "2024/5/15"
console.log(date.toLocaleString('zh-CN')); // "2024/5/15 08:00:00"
日期计算
// 计算时间差
let start = new Date('2024-01-01');
let end = new Date('2024-12-31');
let diffMs = end - start; // 日期相减得到毫秒数
let diffDays = diffMs / (1000 * 60 * 60 * 24);
console.log(diffDays); // 364(或 365,取决于闰年)
// 增加天数
let tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
console.log(tomorrow);
// 计算年龄
function getAge(birthDate) {
let today = new Date();
let birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
let monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
console.log(getAge('1990-05-15'));
Date 静态方法
console.log(Date.now()); // 当前时间戳
console.log(Date.parse('2024-01-15')); // 解析字符串为时间戳
console.log(Date.UTC(2024, 0, 15)); // UTC 时间戳
RegExp 对象
正则表达式用于匹配字符串模式。
创建正则表达式
// 字面量方式(推荐)
let regex1 = /ab+c/;
let regex2 = /\d+/g;
// 构造函数方式(动态创建)
let pattern = 'ab+c';
let regex3 = new RegExp(pattern, 'g');
正则方法
let str = 'Hello 123 World 456';
// test() — 测试是否匹配
console.log(/\d+/.test(str)); // true(包含数字)
console.log(/^[a-z]+$/i.test('Hello')); // true(仅字母)
// exec() — 执行匹配,返回匹配结果或 null
let match = /\d+/.exec(str);
console.log(match[0]); // "123"
console.log(match.index); // 6(匹配位置)
console.log(match.input); // "Hello 123 World 456"
// 全局匹配时,exec 会记录 lastIndex
let regex = /\d+/g;
console.log(regex.exec(str)); // ["123", index: 6, ...]
console.log(regex.exec(str)); // ["456", index: 16, ...]
console.log(regex.exec(str)); // null
// 重置 lastIndex
regex.lastIndex = 0;
字符串中的正则方法(回顾)
let str = 'Hello 123 World 456';
console.log(str.match(/\d+/g)); // ["123", "456"]
console.log(str.replace(/\d+/g, '#')); // "Hello # World #"
console.log(str.search(/\d+/)); // 6(首次匹配位置)
console.log(str.split(/\s+/)); // ["Hello", "123", "World", "456"]
常用正则模式
// 数字
console.log(/^\d+$/.test('123')); // true
console.log(/^\d+$/.test('12a3')); // false
// 邮箱(简化版)
let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRegex.test('[email protected]')); // true
// 手机号(中国大陆,简化版)
let phoneRegex = /^1[3-9]\d{9}$/;
console.log(phoneRegex.test('13812345678')); // true
// 中文字符
let chineseRegex = /[一-龥]+/;
console.log(chineseRegex.test('你好')); // true
JSON 对象
JSON 对象用于处理 JavaScript 对象表示法(JSON)数据。
JSON.parse() — 解析 JSON 字符串
let jsonStr = '{"name":"Alice","age":25,"city":"Beijing"}';
let obj = JSON.parse(jsonStr);
console.log(obj.name); // "Alice"
console.log(obj.age); // 25
// 第二个参数:reviver 函数(转换值)
let json = '{"date":"2024-01-15T00:00:00.000Z"}';
let data = JSON.parse(json, (key, value) => {
if (key === 'date') return new Date(value);
return value;
});
console.log(data.date instanceof Date); // true
// 无效 JSON 会抛出错误
try {
JSON.parse('{invalid json}');
} catch (e) {
console.log('JSON 解析失败:', e.message);
}
JSON.stringify() — 转为 JSON 字符串
let obj = {
name: 'Alice',
age: 25,
city: 'Beijing',
greet: function() { console.log('hello'); } // 函数不会被序列化
};
let jsonStr = JSON.stringify(obj);
console.log(jsonStr); // "{"name":"Alice","age":25,"city":"Beijing"}"
// 注意:函数、undefined、Symbol 会被忽略
// 第二个参数:replacer(过滤或转换)
let filtered = JSON.stringify(obj, ['name', 'age']); // 只序列化指定属性
console.log(filtered); // "{"name":"Alice","age":25}"
// 使用函数转换值
let withTypes = JSON.stringify(obj, (key, value) => {
if (typeof value === 'string') return undefined; // 排除字符串
return value;
});
console.log(withTypes);
// 第三个参数:space(格式化缩进)
let pretty = JSON.stringify(obj, null, 2); // 2 空格缩进
console.log(pretty);
// {
// "name": "Alice",
// "age": 25,
// "city": "Beijing"
// }
JSON 方法速查
| 方法 | 说明 |
|---|---|
JSON.parse(text, reviver?) |
解析 JSON 字符串为 JS 值 |
JSON.stringify(value, replacer?, space?) |
将 JS 值转为 JSON 字符串 |
Map 对象(ES6)
Map 是键值对的集合,键可以是任意类型(对象、函数等)。
基本用法
// 创建 Map
let map = new Map();
// 设置值
map.set('name', 'Alice');
map.set(123, '数字键');
map.set(true, '布尔键');
map.set({ id: 1 }, '对象键');
// 获取值
console.log(map.get('name')); // "Alice"
console.log(map.get(123)); // "数字键"
// 检查键是否存在
console.log(map.has('name')); // true
console.log(map.has('age')); // false
// 删除
map.delete('name');
console.log(map.has('name')); // false
// 大小
console.log(map.size); // 3
// 清空
// map.clear();
使用数组初始化
let map = new Map([
['name', 'Alice'],
['age', 25],
['city', 'Beijing']
]);
console.log(map.get('name')); // "Alice"
遍历 Map
let map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
// for...of 遍历
for (let [key, value] of map) {
console.log(key, value);
}
// forEach 遍历
map.forEach((value, key) => {
console.log(key, value);
});
// 只遍历键
for (let key of map.keys()) {
console.log(key);
}
// 只遍历值
for (let value of map.values()) {
console.log(value);
}
// 转为数组
console.log([...map.keys()]); // ["a", "b", "c"]
console.log([...map.values()]); // [1, 2, 3]
console.log([...map.entries()]); // [["a",1], ["b",2], ["c",3]]
Map vs Object
| 特性 | Map | Object |
|---|---|---|
| 键的类型 | 任意类型 | String 或 Symbol |
| 大小 | size 属性 |
需手动计算 |
| 迭代 | 可直接迭代 | 需转换 |
| 性能 | 频繁增删时更好 | 适合简单场景 |
| 默认键 | 无 | 有原型链上的键 |
Set 对象(ES6)
Set 是值的集合,每个值只出现一次(唯一性)。
基本用法
// 创建 Set
let set = new Set([1, 2, 2, 3, 3, 4]);
console.log(set); // Set { 1, 2, 3, 4 }(自动去重)
// 添加值
set.add(5);
console.log(set.has(3)); // true
console.log(set.has(6)); // false
// 删除值
set.delete(1);
console.log(set.size); // 4
// 清空
// set.clear();
// 遍历
for (let value of set) {
console.log(value);
}
set.forEach(value => console.log(value));
数组去重
// 使用 Set 去重
let arr = [1, 2, 2, 3, 3, 3, 4, 5, 5];
let unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4, 5]
// 或使用 Array.from
let unique2 = Array.from(new Set(arr));
console.log(unique2); // [1, 2, 3, 4, 5]
Set 运算
let setA = new Set([1, 2, 3, 4]);
let setB = new Set([3, 4, 5, 6]);
// 并集
let union = new Set([...setA, ...setB]);
console.log(union); // Set { 1, 2, 3, 4, 5, 6 }
// 交集
let intersection = new Set([...setA].filter(x => setB.has(x)));
console.log(intersection); // Set { 3, 4 }
// 差集(A - B)
let difference = new Set([...setA].filter(x => !setB.has(x)));
console.log(difference); // Set { 1, 2 }
WeakMap 和 WeakSet(ES6)
WeakMap
WeakMap 的键必须是对象,且是"弱引用"(不阻止垃圾回收)。
let weakMap = new WeakMap();
let obj = { name: 'Alice' };
weakMap.set(obj, '一些数据');
console.log(weakMap.get(obj)); // "一些数据"
// weakMap.set('string', 'value'); // 报错:键必须是对象
// 没有 size 属性,不能迭代(弱引用的原因)
// console.log(weakMap.size); // undefined
// for (let item of weakMap) {} // 报错
WeakSet
WeakSet 只能存储对象,且是弱引用。
let weakSet = new WeakSet();
let obj1 = { id: 1 };
let obj2 = { id: 2 };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
// weakSet.add(123); // 报错:只能添加对象
WeakMap/WeakSet 的使用场景
// 使用 WeakMap 存储私有数据
let privateData = new WeakMap();
function User(name) {
let data = { name };
privateData.set(this, data);
}
User.prototype.getName = function() {
return privateData.get(this).name;
};
let user = new User('Alice');
console.log(user.getName()); // "Alice"
// 当 user 被垃圾回收时,privateData 中的对应数据也会被回收
Symbol(ES6)
Symbol 是唯一的、不可变的值,通常用作对象属性的键。
创建 Symbol
// 创建 Symbol(可选描述符)
let sym1 = Symbol();
let sym2 = Symbol('description');
let sym3 = Symbol('description');
console.log(sym1 === sym2); // false(每个 Symbol 都是唯一的)
console.log(sym2 === sym3); // false(即使是相同描述符)
// Symbol.for() — 全局 Symbol 注册表
let sym4 = Symbol.for('app.id');
let sym5 = Symbol.for('app.id');
console.log(sym4 === sym5); // true(从注册表获取)
// Symbol.keyFor() — 获取全局 Symbol 的键
console.log(Symbol.keyFor(sym4)); // "app.id"
使用 Symbol 作为属性键
let id = Symbol('id');
let user = {
name: 'Alice',
[id]: 12345 // Symbol 作为键
};
console.log(user[id]); // 12345
console.log(Object.keys(user)); // ["name"](Symbol 键不会被遍历)
// Symbol 不会被 for...in、Object.keys() 等遍历到
// 获取 Symbol 键:Object.getOwnPropertySymbols()
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]
内置 Symbol
JavaScript 内置了一些 Symbol,用于自定义对象行为:
// Symbol.iterator — 定义迭代器
let obj = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
}
return { done: true };
}
};
}
};
for (let value of obj) {
console.log(value); // 1 2 3
}
// 其他内置 Symbol(了解即可)
// Symbol.toPrimitive — 对象转原始值
// Symbol.toStringTag — Object.prototype.toString 的标签
// Symbol.hasInstance — instanceof 操作符的行为
BigInt(ES2020)
BigInt 用于表示任意精度的整数,解决 Number 精度限制问题。
创建 BigInt
// 方式1:在数字后加 n
let big1 = 12345678901234567890n;
console.log(big1); // 12345678901234567890n
// 方式2:使用 BigInt() 函数
let big2 = BigInt(123);
let big3 = BigInt('456');
console.log(big2); // 123n
console.log(big3); // 456n
// 注意:BigInt 和 Number 不可以直接混合运算
// console.log(big1 + 123); // TypeError
console.log(big1 + BigInt(123)); // 12345678901234568013n
BigInt 运算
let a = 10n;
let b = 3n;
console.log(a + b); // 13n
console.log(a - b); // 7n
console.log(a * b); // 30n
console.log(a / b); // 3n(整数除法,没有小数!)
console.log(a % b); // 1n
console.log(a ** 3n); // 1000n
// 比较(可以和 Number 比较)
console.log(10n === 10); // false(类型不同)
console.log(10n == 10); // true(宽松相等会转换)
console.log(10n > 5); // true
Number 的精度限制
// Number 的最大安全整数
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2); // true(精度丢失!)
// BigInt 没有这个限制
let big = BigInt(Number.MAX_SAFE_INTEGER);
console.log(big + 1n === big + 2n); // false(正确)
Promise 对象(ES6)
Promise 用于处理异步操作。
创建 Promise
let promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
let success = true;
if (success) {
resolve('操作成功'); // 兑现(fulfilled)
} else {
reject(new Error('操作失败')); // 拒绝(rejected)
}
}, 1000);
});
promise
.then(result => console.log('成功:', result))
.catch(error => console.log('失败:', error.message));
Promise 静态方法
// Promise.resolve() — 创建已兑现的 Promise
Promise.resolve('done').then(console.log); // "done"
// Promise.reject() — 创建已拒绝的 Promise
Promise.reject(new Error('fail')).catch(console.error);
// Promise.all() — 所有 Promise 都成功时才成功
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(values => console.log(values)); // [1, 2, 3]
// Promise.allSettled() — 等待所有 Promise 完成(无论成功失败)
Promise.allSettled([p1, Promise.reject('error')]).then(results => {
console.log(results);
// [{status: "fulfilled", value: 1}, {status: "rejected", reason: "error"}]
});
// Promise.race() — 返回最先完成的 Promise(无论成功失败)
Promise.race([
new Promise((resolve) => setTimeout(() => resolve('fast'), 100)),
new Promise((resolve) => setTimeout(() => resolve('slow'), 500))
]).then(console.log); // "fast"
// Promise.any() — 返回最先成功的 Promise(ES2021)
Promise.any([
Promise.reject('error1'),
Promise.resolve('success'),
Promise.reject('error2')
]).then(console.log); // "success"
async/await(ES2017)
// async 函数返回 Promise
async function fetchData() {
// await 等待 Promise
let result = await new Promise(resolve => {
setTimeout(() => resolve('data'), 1000);
});
return result;
}
fetchData().then(console.log); // "data"(1秒后)
注意:Promise 和 async/await 是异步编程的核心,这里仅作简要介绍。
Proxy 和 Reflect(ES6)
Proxy — 创建对象的代理,拦截操作
let target = { name: 'Alice', age: 25 };
let proxy = new Proxy(target, {
// 拦截属性读取
get(target, prop, receiver) {
console.log(`读取属性: ${prop}`);
return Reflect.get(target, prop, receiver);
},
// 拦截属性设置
set(target, prop, value, receiver) {
console.log(`设置属性: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
},
// 拦截属性删除
deleteProperty(target, prop) {
console.log(`删除属性: ${prop}`);
return Reflect.deleteProperty(target, prop);
}
});
proxy.name; // 输出:读取属性: name
proxy.age = 26; // 输出:设置属性: age = 26
delete proxy.age; // 输出:删除属性: age
实用示例:属性验证
function createValidatedObject(obj, validators) {
return new Proxy(obj, {
set(target, prop, value) {
if (validators[prop]) {
let error = validators[prop](value);
if (error) {
console.log(`验证失败 [${prop}]: ${error}`);
return false;
}
}
target[prop] = value;
return true;
}
});
}
let user = createValidatedObject({}, {
age: (value) => value < 0 ? '年龄不能为负数' : null,
name: (value) => value.length < 2 ? '姓名至少2个字符' : null
});
user.name = 'A'; // 验证失败 [name]: 姓名至少2个字符
user.name = 'Alice'; // 设置成功
user.age = -5; // 验证失败 [age]: 年龄不能为负数
user.age = 25; // 设置成功
Reflect — 提供操作对象的反射 API
let obj = { name: 'Alice' };
// Reflect 的方法与 Proxy 的处理器方法一一对应
console.log(Reflect.get(obj, 'name')); // "Alice"
console.log(Reflect.has(obj, 'name')); // true(相当于 'name' in obj)
console.log(Reflect.deleteProperty(obj, 'name')); // true
console.log(Reflect.ownKeys(obj)); // ["name"]
Proxy 可拦截的操作
| 拦截器 | 触发操作 |
|---|---|
get |
读取属性 |
set |
设置属性 |
has |
in 操作符 |
deleteProperty |
delete 操作符 |
ownKeys |
Object.keys()、Object.getOwnPropertyNames() 等 |
getPrototypeOf |
Object.getPrototypeOf() |
setPrototypeOf |
Object.setPrototypeOf() |
isExtensible |
Object.isExtensible() |
preventExtensions |
Object.preventExtensions() |
getOwnPropertyDescriptor |
Object.getOwnPropertyDescriptor() |
defineProperty |
Object.defineProperty() |
apply |
函数调用 |
construct |
new 操作符 |
其他内置对象(简要)
Intl — 国际化(ES6)
// 数字格式化
let number = 1234567.89;
console.log(new Intl.NumberFormat('zh-CN').format(number)); // "1,234,567.89"
// 日期格式化
let date = new Date();
console.log(new Intl.DateTimeFormat('zh-CN').format(date)); // "2024/5/15"
// 字符串比较(排序)
let items = ['中', '文', '排', '序'];
items.sort(new Intl.Collator('zh-CN').compare);
console.log(items); // ["排", "序", "文", "中"](按拼音排序)
WeakRef(ES2021)
// WeakRef 允许创建对对象的弱引用(高级特性,了解即可)
let obj = { data: 'hello' };
let weakRef = new WeakRef(obj);
console.log(weakRef.deref()); // { data: 'hello' }(如果对象还在)
// 如果 obj 被垃圾回收,weakRef.deref() 返回 undefined
FinalizationRegistry(ES2021)
// 对象被垃圾回收时收到通知(高级特性,了解即可)
let registry = new FinalizationRegistry((heldValue) => {
console.log(`对象 ${heldValue} 被回收了`);
});
let obj = { id: 1 };
registry.register(obj, 'id=1');
// 当 obj 被回收时,会打印 "对象 id=1 被回收了"
内置对象速查总表
| 对象 | 类别 | 说明 | 详见 |
|---|---|---|---|
Object |
基础 | 所有对象的基础 | js-data-types.md |
Function |
函数 | 函数构造器和原型 | js-function.md |
Array |
数据结构 | 数组 | js-data-types.md |
String |
基础类型包装 | 字符串 | js-data-types.md |
Number |
基础类型包装 | 数字 | js-data-types.md |
Boolean |
基础类型包装 | 布尔值 | js-data-types.md |
Math |
数学 | 数学常数和函数 | 本文 |
Date |
日期时间 | 日期和时间处理 | 本文 |
RegExp |
文本处理 | 正则表达式 | 本文 |
JSON |
数据格式 | JSON 解析和序列化 | 本文 |
Map |
数据结构(ES6) | 键值对集合 | 本文 |
Set |
数据结构(ES6) | 唯一值集合 | 本文 |
WeakMap |
数据结构(ES6) | 弱引用键值对 | 本文 |
WeakSet |
数据结构(ES6) | 弱引用集合 | 本文 |
Symbol |
原始类型(ES6) | 唯一标识符 | 本文 |
BigInt |
原始类型(ES2020) | 大整数 | 本文 |
Promise |
异步(ES6) | 异步操作 | 本文 |
Proxy |
元编程(ES6) | 对象代理 | 本文 |
Reflect |
元编程(ES6) | 反射 API | 本文 |
Error |
错误 | 错误对象及子类 | js-exception.md |
Intl |
国际化(ES6) | 国际化 API | 本文 |
globalThis |
全局(ES2020) | 统一全局对象 | 本文 |
综合示例
示例 1:数据验证和格式化
function processUserData(jsonString) {
try {
// 解析 JSON
let user = JSON.parse(jsonString);
// 验证数据
let errors = [];
if (!user.name || user.name.trim() === '') {
errors.push('姓名不能为空');
}
if (!Number.isInteger(user.age) || user.age < 0) {
errors.push('年龄必须是非负整数');
}
if (errors.length > 0) {
return { success: false, errors };
}
// 格式化输出
return {
success: true,
data: {
name: user.name.trim(),
age: user.age,
registeredAt: new Date().toISOString()
}
};
} catch (e) {
return { success: false, errors: ['JSON 解析失败: ' + e.message] };
}
}
let result = processUserData('{"name":"Alice","age":25}');
console.log(JSON.stringify(result, null, 2));
示例 2:简单的事件系统(使用 Map)
class EventSystem {
constructor() {
this.events = new Map();
}
on(event, callback) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event).push(callback);
}
emit(event, data) {
let callbacks = this.events.get(event);
if (callbacks) {
callbacks.forEach(cb => cb(data));
}
}
off(event, callback) {
let callbacks = this.events.get(event);
if (callbacks) {
this.events.set(event, callbacks.filter(cb => cb !== callback));
}
}
}
let bus = new EventSystem();
bus.on('message', data => console.log('收到:', data));
bus.emit('message', 'Hello!'); // 收到: Hello!
示例 3:使用 Proxy 实现数据绑定(简化版)
function createReactiveObject(obj, callback) {
return new Proxy(obj, {
set(target, prop, value) {
let oldValue = target[prop];
target[prop] = value;
callback(prop, oldValue, value);
return true;
}
});
}
let state = createReactiveObject({ count: 0 }, (prop, oldVal, newVal) => {
console.log(`状态变化: ${prop} ${oldVal} → ${newVal}`);
});
state.count = 1; // 状态变化: count 0 → 1
state.count = 2; // 状态变化: count 1 → 2
示例 4:随机数和数学应用
// 生成随机颜色
function randomColor() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
return `rgb(${r}, ${g}, ${b})`;
}
console.log(randomColor()); // 如 "rgb(123, 45, 67)"
// 计算两点距离
function distance(x1, y1, x2, y2) {
return Math.hypot(x2 - x1, y2 - y1);
}
console.log(distance(0, 0, 3, 4)); // 5
// 角度转弧度 / 弧度转角度
function degToRad(deg) { return deg * Math.PI / 180; }
function radToDeg(rad) { return rad * 180 / Math.PI; }
console.log(degToRad(180)); // 3.141592653589793(π)
总结
| 类别 | 主要对象 |
|---|---|
| 全局函数 | parseInt、parseFloat、isNaN、isFinite、eval |
| 数学 | Math(floor、ceil、round、random、max、min 等) |
| 日期时间 | Date(get/set 方法、格式化) |
| 正则 | RegExp(test、exec) |
| JSON | JSON.parse、JSON.stringify |
| ES6 数据结构 | Map、Set、WeakMap、WeakSet |
| ES6 原始类型 | Symbol、BigInt |
| ES6 异步 | Promise(all、race、allSettled、any) |
| ES6 元编程 | Proxy、Reflect |
| 国际化 | Intl |
| 全局对象 | globalThis(ES2020) |