4.2.5 字符串方法的应用场景
场景一:数据验证
javascript
// 【代码注释】Validator 组合 `includes`、`startsWith` 与 `RegExp.test`:注意 `includes` 不接受正则,密码强度需用 `/[A-Z]/.test()`。手机号先 `replaceAll` 去分隔符再匹配大陆 11 位规则。
class Validator {
static isEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email) && email.includes('.') && email.includes('@');
}
static isPhoneNumber(phone) {
const cleanPhone = phone.replaceAll(/[\s-()]/g, '');
return /^1[3-9]\d{9}$/.test(cleanPhone);
}
static isIdNumber(id) {
return /^\d{15}$|^\d{17}[\dXx]$/.test(id);
}
static isUrl(url) {
return url.startsWith('http://') ||
url.startsWith('https://') ||
url.startsWith('ftp://');
}
static isStrongPassword(password) {
if (password.length < 8) return false;
if (!/[A-Z]/.test(password)) return false; // includes() 不接受正则,必须用 RegExp.test()
if (!/[a-z]/.test(password)) return false;
if (!/[0-9]/.test(password)) return false;
return true;
}
}
// 使用示例
console.log(Validator.isEmail('test@example.com')); // true
console.log(Validator.isPhoneNumber('138 1234 5678')); // true
【代码注释】Validator 组合 includes、startsWith 与 RegExp.test:注意 includes 不接受正则,密码强度需用 /[A-Z]/.test()。手机号先 replaceAll 去分隔符再匹配大陆 11 位规则。
场景二:文本处理
javascript
// 【代码注释】静态方法封装 truncate/capitalize/camelCase/slug/highlightKeywords:展示字符串 API 组合成业务工具链。`highlightKeywords` 对用户关键词应转义再入 RegExp,防 ReDoS 与 XSS。
class TextProcessor {
// 截断文本
static truncate(text, maxLength, suffix = '...') {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength - suffix.length) + suffix;
}
// 首字母大写
static capitalize(text) {
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
}
// 驼峰命名转换
static toCamelCase(text) {
return text
.split(/[\s_-]+/)
.map((word, index) => {
if (index === 0) return word.toLowerCase();
return this.capitalize(word);
})
.join('');
}
// 生成slug
static generateSlug(text) {
return text
.toLowerCase()
.trim()
.replace(/[\s_]+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '');
}
// 高亮关键词
static highlightKeywords(text, keywords, className = 'highlight') {
let result = text;
keywords.forEach(keyword => {
const regex = new RegExp(`(${keyword})`, 'gi');
result = result.replace(regex, `<span class="${className}">$1</span>`);
});
return result;
}
}
// 使用示例
console.log(TextProcessor.truncate('这是一段很长的文本,需要被截断', 10));
console.log(TextProcessor.toCamelCase('hello_world_example'));
console.log(TextProcessor.generateSlug('Hello World! This is a Test'));
【代码注释】静态方法封装 truncate/capitalize/camelCase/slug/highlightKeywords:展示字符串 API 组合成业务工具链。highlightKeywords 对用户关键词应转义再入 RegExp,防 ReDoS 与 XSS。
4.3 完整可运行示例:模板字符串与字符串方法
4.3.1 模板字符串:换行、插值与列表 DOM 渲染
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>模板字符串演示</title>
</head>
<body>
<ul id="box">
<li><span>1</span><a href="#">占位</a></li>
</ul>
<script>
// 【代码注释】反引号 `` 创建模板字符串,类型仍为 string
const msg = `Hello World`;
console.log(typeof msg, msg);
// 【代码注释】多行文本:换行与缩进会原样保留,无需 \n 与 + 拼接
const content = `
锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
`;
console.log(content);
const name = '高小乐';
const gender = '男';
const hometown = '山西';
const hobby = '醋';
const money = 0.25;
// 【代码注释】${} 内可为任意表达式(算术、变量引用等),先求值再转为字符串插入
const message = `
我的名字叫${name},
性别${gender},
我来自${hometown},
我的业余爱好是${hobby},
我会进行运算 ${money + 78 * 2}
我学习后的期望薪资是 ${money} K;`;
console.log(message);
// 【代码注释】map + 模板字符串:列表渲染经典模式(注意 XSS:用户数据需转义)
const data = ['刘姥姥', '马姥姥', '司马姥姥', '欧阳姥姥', '宇文姥姥'];
const html = data.map(function (item, index) {
return `
<li>
<span>${index + 1}</span>
<a href="#">${item}</a>
</li>`;
}).join(''); // join('') 拼成无分隔符的 HTML 片段
document.querySelector('#box').innerHTML = html;
</script>
</body>
</html>
【代码注释】本页演示模板字符串三要素:多行保留换行 (古诗示例)、${} 表达式插值 (含算术 money + 78 * 2)、map 生成 HTML 列表 (join('') 拼片段后写 innerHTML)。注意:用户输入插入 DOM 前须转义防 XSS,或使用框架自动转义。与 Vue {``{ }}、React JSX { } 同属「数据驱动视图」思想,是 Day01 最接近真实业务的 HTML 实验页。
业界应用:
- Vue :
{``{ message }}与指令本质是模板引擎 - React :JSX 中
{user.name}与${}同属插值思想 - 邮件/日志 :多行模板 + 变量拼接,可读性远高于
+连接
4.3.2 字符串实例方法:repeat、includes、pad、trim、replaceAll
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>字符串新方法演示</title>
</head>
<body>
<script>
// 【代码注释】repeat(n):将字符串重复 n 次,n 须为非负整数
console.log('高小乐'.repeat(10));
const msg = 'Hello World';
// 【代码注释】includes(search, fromIndex?):返回 boolean;第二参数为起始搜索下标
console.log(msg.includes('l'), msg.includes('World'), msg.includes('H', 5));
// 【代码注释】startsWith:判前缀;第二参数为开始匹配的位置
console.log(msg.startsWith('Hello'), msg.startsWith('Hello', 1));
// 【代码注释】endsWith:判后缀;第二参数为「结束位置」前的子串
console.log(msg.endsWith('d'), msg.endsWith('d', 5));
// 【代码注释】padStart/padEnd:填充到目标长度,常用于补零、对齐
console.log(msg.padStart(20), msg.padStart(20, '@'));
console.log(msg.padEnd(30), msg.padEnd(30, '0'));
const content = ' Hello World ';
// 【代码注释】trim 系列:去除 Unicode 空白;不改变原串,返回新串
console.log(`#${content}#`, `#${content.trim()}#`);
console.log(`#${content.trimStart()}#`, `#${content.trimEnd()}#`);
// 【代码注释】replace 只换首个;replaceAll(ES2021)全局替换
console.log(content.replace('l', 'L'));
console.log(content.replaceAll('l', 'L'));
</script>
</body>
</html>
【代码注释】本页逐行演示 ES6+ 字符串实例方法:repeat 生成分隔线;includes/startsWith/endsWith 做路由、扩展名、关键字检测;padStart 左补零(订单号);trim* 表单清洗;replace vs replaceAll 区分「首个」与「全部」。注意:所有方法均 不修改原字符串 ,返回新串;includes 区分大小写,忽略大小写需先 toLowerCase()。
| 方法 | 返回值 | 经典场景 |
|---|---|---|
repeat(n) |
新字符串 | 生成分隔线、占位块 |
includes/search, pos?) |
boolean | 搜索框过滤、权限关键字检测 |
startsWith/endsWith |
boolean | URL/路由前缀、文件扩展名 |
padStart/padEnd |
新字符串 | 账单编号、控制台对齐 |
trim/trimStart/trimEnd |
新字符串 | 表单校验前清洗空格 |
replaceAll |
新字符串 | 批量替换敏感词、模板占位符 |
5. 数值新增特性:更精确的数字计算
5.1 新增的二进制和八进制表示方式
5.1.1 新的数值字面量语法
名词解析:
- 二进制字面量(Binary Literal):使用0b或0B前缀表示二进制数
- 八进制字面量(Octal Literal):使用0o或0O前缀表示八进制数
- 十六进制字面量(Hexadecimal Literal):使用0x或0X前缀表示十六进制数
javascript
// 【代码注释】`0b`/`0B` 二进制、`0o`/`0O` 八进制字面量,避免 ES5 无前缀八进制 `012` 的歧义。权限位、颜色 `0xFF` 与位运算 `& | <<` 配合;`rgbToHex` 用移位拼 24 位色值。
// 二进制表示
const binary1 = 0b1010; // 10
const binary2 = 0B1111; // 15
// 八进制表示
const octal1 = 0o17; // 15
const octal2 = 0O10; // 8
// 十六进制表示(ES5就支持)
const hex1 = 0xFF; // 255
const hex2 = 0x10; // 16
console.log(binary1, binary2, octal1, octal2, hex1, hex2); // 10 15 15 8 255 16
// 实际应用:位操作
const permissions = 0b10101010; // 文件权限
const readPermission = 0b10000000;
const writePermission = 0b01000000;
const hasRead = (permissions & readPermission) !== 0;
const hasWrite = (permissions & writePermission) !== 0;
console.log('可读:', hasRead); // true
console.log('可写:', hasWrite); // true
// 颜色值处理
const colorRed = 0xFF0000; // 红色
const colorGreen = 0x00FF00; // 绿色
const colorBlue = 0x0000FF; // 蓝色
function rgbToHex(r, g, b) {
return (r << 16) + (g << 8) + b;
}
const purple = rgbToHex(0xFF, 0x00, 0xFF); // 0xFF00FF
console.log(purple.toString(16)); // ff00ff
【代码注释】0b/0B 二进制、0o/0O 八进制字面量,避免 ES5 无前缀八进制 012 的歧义。权限位、颜色 0xFF 与位运算 & | << 配合;rgbToHex 用移位拼 24 位色值。
5.2 Number构造函数新增方法和属性
5.2.1 静态方法
#mermaid-svg-VH512DzB8zwmbaC0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VH512DzB8zwmbaC0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VH512DzB8zwmbaC0 .error-icon{fill:#552222;}#mermaid-svg-VH512DzB8zwmbaC0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VH512DzB8zwmbaC0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VH512DzB8zwmbaC0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VH512DzB8zwmbaC0 .marker.cross{stroke:#333333;}#mermaid-svg-VH512DzB8zwmbaC0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VH512DzB8zwmbaC0 p{margin:0;}#mermaid-svg-VH512DzB8zwmbaC0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VH512DzB8zwmbaC0 .cluster-label text{fill:#333;}#mermaid-svg-VH512DzB8zwmbaC0 .cluster-label span{color:#333;}#mermaid-svg-VH512DzB8zwmbaC0 .cluster-label span p{background-color:transparent;}#mermaid-svg-VH512DzB8zwmbaC0 .label text,#mermaid-svg-VH512DzB8zwmbaC0 span{fill:#333;color:#333;}#mermaid-svg-VH512DzB8zwmbaC0 .node rect,#mermaid-svg-VH512DzB8zwmbaC0 .node circle,#mermaid-svg-VH512DzB8zwmbaC0 .node ellipse,#mermaid-svg-VH512DzB8zwmbaC0 .node polygon,#mermaid-svg-VH512DzB8zwmbaC0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VH512DzB8zwmbaC0 .rough-node .label text,#mermaid-svg-VH512DzB8zwmbaC0 .node .label text,#mermaid-svg-VH512DzB8zwmbaC0 .image-shape .label,#mermaid-svg-VH512DzB8zwmbaC0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-VH512DzB8zwmbaC0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VH512DzB8zwmbaC0 .rough-node .label,#mermaid-svg-VH512DzB8zwmbaC0 .node .label,#mermaid-svg-VH512DzB8zwmbaC0 .image-shape .label,#mermaid-svg-VH512DzB8zwmbaC0 .icon-shape .label{text-align:center;}#mermaid-svg-VH512DzB8zwmbaC0 .node.clickable{cursor:pointer;}#mermaid-svg-VH512DzB8zwmbaC0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VH512DzB8zwmbaC0 .arrowheadPath{fill:#333333;}#mermaid-svg-VH512DzB8zwmbaC0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VH512DzB8zwmbaC0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VH512DzB8zwmbaC0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VH512DzB8zwmbaC0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VH512DzB8zwmbaC0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VH512DzB8zwmbaC0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VH512DzB8zwmbaC0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VH512DzB8zwmbaC0 .cluster text{fill:#333;}#mermaid-svg-VH512DzB8zwmbaC0 .cluster span{color:#333;}#mermaid-svg-VH512DzB8zwmbaC0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VH512DzB8zwmbaC0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VH512DzB8zwmbaC0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-VH512DzB8zwmbaC0 .icon-shape,#mermaid-svg-VH512DzB8zwmbaC0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VH512DzB8zwmbaC0 .icon-shape p,#mermaid-svg-VH512DzB8zwmbaC0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VH512DzB8zwmbaC0 .icon-shape .label rect,#mermaid-svg-VH512DzB8zwmbaC0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VH512DzB8zwmbaC0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VH512DzB8zwmbaC0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VH512DzB8zwmbaC0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Number新增方法
类型检查
类型转换
Number.isFinite
Number.isNaN
Number.isInteger
Number.isSafeInteger
Number.parseFloat
Number.parseInt
【代码注释】Number 静态方法分为「类型检查」(isFinite/isNaN/isInteger 等)与「解析转换」(parseInt/parseFloat),比全局函数更严格、不隐式转型。
5.2.2 类型检查方法
方法一:Number.isFinite()
javascript
// 【代码注释】`Number.isFinite` 仅对 **number 类型**且非 ±Infinity/NaN 为 true;`isFinite('100')` 会先 ToNumber 再判断。表单校验:`const n = Number(v); Number.isFinite(n)` 比全局 `isFinite` 更严。
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(100.5)); // true
console.log(Number.isFinite('100')); // false
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(NaN)); // false
// 与全局isFinite()的区别
console.log(isFinite('100')); // true(会自动转换)
console.log(Number.isFinite('100')); // false(不转换)
// 实际应用:数值验证
function validateNumberInput(input) {
if (!Number.isFinite(input)) {
throw new Error('输入必须是有效的数字');
}
if (input < 0 || input > 100) {
throw new Error('输入必须在0-100之间');
}
return input;
}
try {
const result = validateNumberInput(50);
console.log('有效输入:', result);
} catch (error) {
console.error(error.message);
}
【代码注释】Number.isFinite 仅对 number 类型 且非 ±Infinity/NaN 为 true;isFinite('100') 会先 ToNumber 再判断。表单校验:const n = Number(v); Number.isFinite(n) 比全局 isFinite 更严。
方法二:Number.isNaN()
javascript
// 【代码注释】仅 `Number.isNaN(NaN)` 为 true,`Number.isNaN('NaN')` 为 false;全局 `isNaN('NaN')` 为 true。判断运算结果是否无效时用 `Number.isNaN(result)`,不要用 `result === NaN`(恒 false)。
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('NaN')); // false
console.log(Number.isNaN(undefined)); // false
// 与全局isNaN()的区别
console.log(isNaN('NaN')); // true(会尝试转换)
console.log(Number.isNaN('NaN')); // false(严格检查)
// 实际应用:计算结果验证
function safeDivide(a, b) {
const result = a / b;
if (Number.isNaN(result)) {
return { success: false, error: '计算结果无效' };
}
if (!Number.isFinite(result)) {
return { success: false, error: '计算结果超出范围' };
}
return { success: true, result };
}
console.log(safeDivide(10, 2)); // { success: true, result: 5 }
console.log(safeDivide(10, 0)); // { success: false, error: '计算结果超出范围' }
【代码注释】仅 Number.isNaN(NaN) 为 true,Number.isNaN('NaN') 为 false;全局 isNaN('NaN') 为 true。判断运算结果是否无效时用 Number.isNaN(result),不要用 result === NaN(恒 false)。
方法三:Number.isInteger()
javascript
// 【代码注释】`Number.isInteger` 要求值为 number 且小数部分为 0;`100.0` 为 true。数量、数组下标校验用;字符串 `"100"` 需先 `Number()` 再判。与 `parseInt` 不同,不解析字符串。
console.log(Number.isInteger(100)); // true
console.log(Number.isInteger(100.5)); // false
console.log(Number.isInteger('100')); // false
// 实际应用:整数验证
function processQuantity(quantity) {
if (!Number.isInteger(quantity) || quantity < 1) {
throw new Error('数量必须是正整数');
}
// 处理逻辑
return `处理 ${quantity} 件商品`;
}
// 数组索引验证
function getArrayItem(arr, index) {
if (!Number.isInteger(index) || index < 0) {
throw new Error('索引必须是非负整数');
}
if (index >= arr.length) {
throw new Error('索引超出范围');
}
return arr[index];
}
const fruits = ['苹果', '香蕉', '橙子'];
console.log(getArrayItem(fruits, 1)); // 香蕉
【代码注释】Number.isInteger 要求值为 number 且小数部分为 0;100.0 为 true。数量、数组下标校验用;字符串 "100" 需先 Number() 再判。与 parseInt 不同,不解析字符串。
方法四:Number.isSafeInteger()
javascript
// 【代码注释】安全整数范围 ±(2^53−1);超出后 `a±1` 可能相等(精度丢失)。ID、计数器在 JSON/API 传递前用 `isSafeInteger`;更大整数用 BigInt。`safeAdd` 在运算后再检结果是否仍安全。
console.log(Number.isSafeInteger(100)); // true
console.log(Number.isSafeInteger(Math.pow(2, 53) - 1)); // true
console.log(Number.isSafeInteger(Math.pow(2, 53))); // false
// 安全整数范围:-(2^53 - 1) 到 2^53 - 1
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER; // 9007199254740991
const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER; // -9007199254740991
// 实际应用:精确计算验证
function safeAdd(a, b) {
if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
throw new Error('操作数超出安全整数范围');
}
const result = a + b;
if (!Number.isSafeInteger(result)) {
throw new Error('计算结果超出安全整数范围');
}
return result;
}
// ID验证
function validateUserId(id) {
if (!Number.isSafeInteger(id) || id < 1) {
return { valid: false, error: '无效的用户ID' };
}
return { valid: true };
}
【代码注释】安全整数范围 ±(2^53−1);超出后 a±1 可能相等(精度丢失)。ID、计数器在 JSON/API 传递前用 isSafeInteger;更大整数用 BigInt。safeAdd 在运算后再检结果是否仍安全。
5.2.2.1 Number.EPSILON 与浮点精度陷阱
理论背景:IEEE 754 双精度浮点数
JavaScript 的 Number 类型基于 IEEE 754 双精度浮点数(64位) ,用二进制近似表示十进制小数,导致某些小数运算结果不精确。Number.EPSILON(ES6)是可表示的最小误差精度(约 2.22e-16),用于安全比较浮点数。
javascript
// 【代码注释】IEEE 754 双精度无法精确表示部分十进制小数,运算会产生舍入误差
console.log(0.1 + 0.2); // 0.30000000000000004(肉眼期望 0.3,实际略大)
console.log(0.1 + 0.2 === 0.3); // false ------ 禁止用 === 直接比较金额/坐标等浮点结果
// 【代码注释】Number.EPSILON ≈ 2.22e-16:相邻两个可区分浮点数的最小间距,用作默认容差
console.log(Number.EPSILON); // 2.220446049250313e-16
// 【代码注释】floatEqual:|a-b| < epsilon 视为相等;业务可按精度改用 1e-9、0.001 等
function floatEqual(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) < epsilon;
}
console.log(floatEqual(0.1 + 0.2, 0.3)); // true ✅
// 【代码注释】价格比较:按业务约定小数位设容差(此处 1e-9 适合「元」级两位小数场景)
function priceEqual(priceA, priceB) {
return Math.abs(priceA - priceB) < 1e-9;
}
console.log(priceEqual(19.99 * 3, 59.97)); // true(直接 === 可能为 false)
// 【代码注释】方案一「转分运算」:先乘 100 取整到分,整数相加再除回元------收银、订单合计首选
function addMoney(a, b) {
const factor = 100;
return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}
console.log(addMoney(0.1, 0.2)); // 0.3 ✅
// 【代码注释】方案二 toFixed:仅用于展示,返回字符串,参与运算前需 Number()
console.log((0.1 + 0.2).toFixed(1)); // "0.3"
// 【代码注释】方案三 BigInt:以「分」为单位存 BigInt,避免 number 超过 2^53 或浮点误差(见 5.5 节)
const price1 = BigInt(Math.round(0.1 * 100)); // 10n
const price2 = BigInt(Math.round(0.2 * 100)); // 20n
console.log(Number(price1 + price2) / 100); // 0.3 ✅
// 【代码注释】Number 静态常量:边界判断、除零、无效运算时使用
console.log(Number.MAX_VALUE); // 约 1.8e+308
console.log(Number.MIN_VALUE); // 最小正数(非最小负数)
console.log(Number.POSITIVE_INFINITY); // 1/0 等
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.NaN); // 0/0、无效 parse;NaN !== NaN
【代码注释】0.1 + 0.2 !== 0.3 源于 IEEE 754 二进制浮点 的表示限制,不是引擎 Bug。生产实践:比较 用 Math.abs(a-b) < 容差 或 Number.EPSILON;金额 用「转分整数」或 BigInt;展示 用 toFixed/Intl.NumberFormat,勿把 toFixed 结果再当 number 精确运算。与 Number.isFinite/isNaN 配合可过滤 Infinity/NaN 污染。
5.2.3 类型转换方法
方法:Number.parseInt()和Number.parseFloat()
javascript
// 【代码注释】`Number.parseInt`/`parseFloat` 与全局同名函数相同,便于 `import { parseInt } from 'number-polyfill'` 式模块化。`parseInt('100px')` 从左解析;`0x` 前缀识别十六进制。不要用它代替严格的 `Number()` 校验。
console.log(Number.parseInt('123')); // 123
console.log(Number.parseFloat('123.45')); // 123.45
console.log(Number.parseInt('123px')); // 123
console.log(Number.parseFloat('123.45px')); // 123.45
// 与全局方法相同
console.log(Number.parseInt === parseInt); // true
console.log(Number.parseFloat === parseFloat); // true
// 实际应用:解析CSS值
function parseCssValue(value) {
const number = parseFloat(value);
const unit = value.slice(String(number).length);
return { number, unit };
}
console.log(parseCssValue('100px')); // { number: 100, unit: 'px' }
console.log(parseCssValue('1.5em')); // { number: 1.5, unit: 'em' }
// 解析文件大小
function parseFileSize(size) {
const number = parseFloat(size);
const unit = size.slice(String(number).length).toUpperCase();
const multipliers = {
'B': 1,
'KB': 1024,
'MB': 1024 * 1024,
'GB': 1024 * 1024 * 1024
};
return number * (multipliers[unit] || 1);
}
console.log(parseFileSize('100KB')); // 102400
console.log(parseFileSize('1.5MB')); // 1572864
【代码注释】Number.parseInt/parseFloat 与全局同名函数相同,便于 import { parseInt } from 'number-polyfill' 式模块化。parseInt('100px') 从左解析;0x 前缀识别十六进制。不要用它代替严格的 Number() 校验。
5.3 Math新增方法
5.3.1 Math对象扩展概览
#mermaid-svg-0YPuLdftpp1TSM9C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0YPuLdftpp1TSM9C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0YPuLdftpp1TSM9C .error-icon{fill:#552222;}#mermaid-svg-0YPuLdftpp1TSM9C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0YPuLdftpp1TSM9C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0YPuLdftpp1TSM9C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0YPuLdftpp1TSM9C .marker.cross{stroke:#333333;}#mermaid-svg-0YPuLdftpp1TSM9C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0YPuLdftpp1TSM9C p{margin:0;}#mermaid-svg-0YPuLdftpp1TSM9C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0YPuLdftpp1TSM9C .cluster-label text{fill:#333;}#mermaid-svg-0YPuLdftpp1TSM9C .cluster-label span{color:#333;}#mermaid-svg-0YPuLdftpp1TSM9C .cluster-label span p{background-color:transparent;}#mermaid-svg-0YPuLdftpp1TSM9C .label text,#mermaid-svg-0YPuLdftpp1TSM9C span{fill:#333;color:#333;}#mermaid-svg-0YPuLdftpp1TSM9C .node rect,#mermaid-svg-0YPuLdftpp1TSM9C .node circle,#mermaid-svg-0YPuLdftpp1TSM9C .node ellipse,#mermaid-svg-0YPuLdftpp1TSM9C .node polygon,#mermaid-svg-0YPuLdftpp1TSM9C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0YPuLdftpp1TSM9C .rough-node .label text,#mermaid-svg-0YPuLdftpp1TSM9C .node .label text,#mermaid-svg-0YPuLdftpp1TSM9C .image-shape .label,#mermaid-svg-0YPuLdftpp1TSM9C .icon-shape .label{text-anchor:middle;}#mermaid-svg-0YPuLdftpp1TSM9C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0YPuLdftpp1TSM9C .rough-node .label,#mermaid-svg-0YPuLdftpp1TSM9C .node .label,#mermaid-svg-0YPuLdftpp1TSM9C .image-shape .label,#mermaid-svg-0YPuLdftpp1TSM9C .icon-shape .label{text-align:center;}#mermaid-svg-0YPuLdftpp1TSM9C .node.clickable{cursor:pointer;}#mermaid-svg-0YPuLdftpp1TSM9C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0YPuLdftpp1TSM9C .arrowheadPath{fill:#333333;}#mermaid-svg-0YPuLdftpp1TSM9C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0YPuLdftpp1TSM9C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0YPuLdftpp1TSM9C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0YPuLdftpp1TSM9C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0YPuLdftpp1TSM9C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0YPuLdftpp1TSM9C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0YPuLdftpp1TSM9C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0YPuLdftpp1TSM9C .cluster text{fill:#333;}#mermaid-svg-0YPuLdftpp1TSM9C .cluster span{color:#333;}#mermaid-svg-0YPuLdftpp1TSM9C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0YPuLdftpp1TSM9C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0YPuLdftpp1TSM9C rect.text{fill:none;stroke-width:0;}#mermaid-svg-0YPuLdftpp1TSM9C .icon-shape,#mermaid-svg-0YPuLdftpp1TSM9C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0YPuLdftpp1TSM9C .icon-shape p,#mermaid-svg-0YPuLdftpp1TSM9C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0YPuLdftpp1TSM9C .icon-shape .label rect,#mermaid-svg-0YPuLdftpp1TSM9C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0YPuLdftpp1TSM9C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0YPuLdftpp1TSM9C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0YPuLdftpp1TSM9C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Math新增方法
数值处理
数学运算
trunc
sign
cbrt
对数函数
双曲函数
位运算
【代码注释】Math 新增方法覆盖取整(trunc)、符号(sign)、立方根(cbrt)等,常与位运算、科学计算、图形坐标处理配合使用。
5.3.2 常用Math方法详解
方法一:Math.trunc()
javascript
// 【代码注释】`Math.trunc` 向零截断,`-3.7` → `-3`;`Math.floor` 向负无穷。正数时 trunc 与 floor 同,负数不同。展示整数部分、分页计算用 trunc 更贴近「去掉小数」语义。
console.log(Math.trunc(4.9)); // 4
console.log(Math.trunc(-4.9)); // -4
console.log(Math.trunc(0.123)); // 0
// 与其他方法的区别
console.log(Math.floor(4.9)); // 4(向下取整)
console.log(Math.ceil(4.1)); // 5(向上取整)
console.log(Math.round(4.5)); // 5(四舍五入)
// 实际应用:处理价格
function calculateDiscount(price, discountPercent) {
const discount = price * (discountPercent / 100);
const finalPrice = price - Math.trunc(discount * 100) / 100;
return finalPrice;
}
console.log(calculateDiscount(99.99, 15)); // 86.99
【代码注释】Math.trunc 向零截断,-3.7 → -3;Math.floor 向负无穷。正数时 trunc 与 floor 同,负数不同。展示整数部分、分页计算用 trunc 更贴近「去掉小数」语义。
方法二:Math.sign()
javascript
// 【代码注释】`Math.sign` 返回 -1/0/1 或 -0/+0 区分;`NaN` 返回 NaN。比 `x>0?1:x<0?-1:0` 简洁。注意 `sign(-0)` 为 -0,极少场景需 `Object.is` 判 -0。
console.log(Math.sign(100)); // 1(正数)
console.log(Math.sign(-100)); // -1(负数)
console.log(Math.sign(0)); // 0
console.log(Math.sign(-0)); // -0
// 实际应用:排序和比较
function compareNumbers(a, b) {
const diff = a - b;
if (Math.abs(diff) < 0.0001) return 0; // 近似相等
return Math.sign(diff);
}
// 符号函数的应用
function formatNumber(num) {
const sign = Math.sign(num);
const absNum = Math.abs(num);
return `${sign === -1 ? '-' : '+'}${absNum.toFixed(2)}`;
}
console.log(formatNumber(123.456)); // +123.46
console.log(formatNumber(-78.9)); // -78.90
【代码注释】Math.sign 返回 -1/0/1 或 -0/+0 区分;NaN 返回 NaN。比 x>0?1:x<0?-1:0 简洁。注意 sign(-0) 为 -0,极少场景需 Object.is 判 -0。
方法三:Math.cbrt()
javascript
// 【代码注释】`Math.cbrt` 可处理负数(与 `Math.pow(x,1/3)` 对负数行为不同)。体积、三维缩放因子开立方根时用;大数注意仍受 number 精度限制。
console.log(Math.cbrt(8)); // 2
console.log(Math.cbrt(27)); // 3
console.log(Math.cbrt(-8)); // -2
// 实际应用:体积计算
function calculateCubeVolume(side) {
return Math.pow(side, 3);
}
function calculateSideFromVolume(volume) {
return Math.cbrt(volume);
}
const volume = 125;
const side = calculateSideFromVolume(volume);
console.log(`体积为${volume}的立方体边长为${side}`); // 边长为5
// 几何计算
function calculateSphereDiameter(volume) {
// V = (4/3) * π * r³
const radius = Math.cbrt((3 * volume) / (4 * Math.PI));
return radius * 2;
}
【代码注释】Math.cbrt 可处理负数(与 Math.pow(x,1/3) 对负数行为不同)。体积、三维缩放因子开立方根时用;大数注意仍受 number 精度限制。
5.3.3 Math方法的实际应用
场景一:统计和数据分析
javascript
// 【代码注释】示例对数组 `reduce` 求和/极值,配合 `Math.min`/`Math.max` spread。大数据集应用单次遍历求 min/max 避免展开栈溢出;统计方差、标准差可在此基础上扩展。
class Statistics {
static mean(numbers) {
const sum = numbers.reduce((acc, num) => acc + num, 0);
return sum / numbers.length;
}
static standardDeviation(numbers) {
const avg = this.mean(numbers);
const squareDiffs = numbers.map(num => Math.pow(num - avg, 2));
const avgSquareDiff = this.mean(squareDiffs);
return Math.sqrt(avgSquareDiff);
}
static clamp(number, min, max) {
return Math.min(Math.max(number, min), max);
}
static normalize(value, min, max) {
return (value - min) / (max - min);
}
static lerp(start, end, t) {
return start + (end - start) * this.clamp(t, 0, 1);
}
}
// 使用示例
const data = [1, 2, 3, 4, 5];
console.log('平均值:', Statistics.mean(data));
console.log('标准差:', Statistics.standardDeviation(data));
// 动画插值
function animateValue(start, end, duration) {
const startTime = performance.now();
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const value = Statistics.lerp(start, end, progress);
console.log('当前值:', value);
if (progress < 1) {
requestAnimationFrame(update);
}
}
requestAnimationFrame(update);
}
【代码注释】示例对数组 reduce 求和/极值,配合 Math.min/Math.max spread。大数据集应用单次遍历求 min/max 避免展开栈溢出;统计方差、标准差可在此基础上扩展。
场景二:游戏开发
javascript
// 【代码注释】向量长度、距离、角度换算展示 Math 与 ES6 解构/模板字符串组合。实际游戏引擎多用专用库(gl-matrix);此处理解 `hypot`、`atan2` 用途即可。
class GameMath {
// 计算两点距离
static distance(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
// 角度转换
static degreesToRadians(degrees) {
return degrees * (Math.PI / 180);
}
static radiansToDegrees(radians) {
return radians * (180 / Math.PI);
}
// 随机数生成
static randomRange(min, max) {
return Math.random() * (max - min) + min;
}
static randomInt(min, max) {
return Math.floor(this.randomRange(min, max + 1));
}
// 弹性碰撞
static elasticCollision(m1, v1, m2, v2) {
const newV1 = ((m1 - m2) * v1 + 2 * m2 * v2) / (m1 + m2);
const newV2 = ((m2 - m1) * v2 + 2 * m1 * v1) / (m1 + m2);
return { v1: newV1, v2: newV2 };
}
}
// 游戏循环
class GameObject {
constructor(x, y, vx, vy) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.mass = 1;
}
update(deltaTime) {
this.x += this.vx * deltaTime;
this.y += this.vy * deltaTime;
}
checkBoundary(width, height) {
if (this.x < 0 || this.x > width) {
this.vx *= -1;
}
if (this.y < 0 || this.y > height) {
this.vy *= -1;
}
}
}
【代码注释】向量长度、距离、角度换算展示 Math 与 ES6 解构/模板字符串组合。实际游戏引擎多用专用库(gl-matrix);此处理解 hypot、atan2 用途即可。
5.4 指数运算符 **
5.4.1 指数运算符基本用法
javascript
// 【代码注释】`**` 右结合:`2**3**2` 即 `2**(3**2)`。`Math.pow` 对 BigInt 不适用;BigInt 用 `**n` 同类。负数底数非整数指数可能产生 NaN(复数不在实数域)。
console.log(2 ** 3); // 8
console.log(3 ** 2); // 9
console.log(4 ** 0.5); // 2(平方根)
console.log(8 ** (1/3)); // 2(立方根)
// 与Math.pow()的比较
console.log(2 ** 3 === Math.pow(2, 3)); // true
// 右结合性
console.log(2 ** 3 ** 2); // 512(等于 2 ** (3 ** 2))
console.log((2 ** 3) ** 2); // 64
// 实际应用:科学计算
function calculateCompoundInterest(principal, rate, time) {
return principal * (1 + rate) ** time;
}
console.log(calculateCompoundInterest(1000, 0.05, 10)); // 1628.89
// 单位转换
function convertBytesToKB(bytes) {
return bytes / (1024 ** 1);
}
function convertBytesToMB(bytes) {
return bytes / (1024 ** 2);
}
function convertBytesToGB(bytes) {
return bytes / (1024 ** 3);
}
console.log(convertBytesToMB(1048576)); // 1
console.log(convertBytesToGB(1073741824)); // 1
【代码注释】** 右结合:2**3**2 即 2**(3**2)。Math.pow 对 BigInt 不适用;BigInt 用 **n 同类。负数底数非整数指数可能产生 NaN(复数不在实数域)。
5.5 新增原始数据类型BigInt
5.5.1 BigInt的基本概念
名词解析:
- BigInt:ES2020新增的原始数据类型,用于表示任意大小的整数
- 安全整数范围 :Number类型的安全范围是-(253-1)到253-1
- 精度丢失:超出安全整数范围的计算可能不精确
javascript
// 【代码注释】字面量后缀 `n` 或 `BigInt(123)`;typeof 为 `bigint`。不能与 number 混算(抛 TypeError),比较可先转同类型。用于超 2^53 的 ID、链上整数。
// 创建BigInt的方式
const bigInt1 = 9007199254740993n; // 使用n后缀
const bigInt2 = BigInt(9007199254740993); // 使用BigInt()函数
const bigInt3 = BigInt('9007199254740993'); // 从字符串创建
console.log(bigInt1 === bigInt2); // true
console.log(bigInt2 === bigInt3); // true
// BigInt vs Number的安全范围
const maxSafe = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(maxSafe + 1); // 9007199254740992(正确)
console.log(maxSafe + 2); // 9007199254740992(错误,精度丢失)
const bigIntMax = BigInt(maxSafe);
console.log(bigIntMax + 1n); // 9007199254740992n(正确)
console.log(bigIntMax + 2n); // 9007199254740993n(正确)
【代码注释】字面量后缀 n 或 BigInt(123);typeof 为 bigint。不能与 number 混算(抛 TypeError),比较可先转同类型。用于超 2^53 的 ID、链上整数。
5.5.2 BigInt的运算和限制
javascript
// 【代码注释】支持 `+ - * ** %`;除法向零取整。混合 `1n + 1` 报错,需显式 `BigInt(1)` 或 `Number(1n)`(大数可能丢精度)。JSON.stringify 默认不支持 BigInt,需自定义 replacer。
const a = 100n;
const b = 50n;
// 基本运算
console.log(a + b); // 150n
console.log(a - b); // 50n
console.log(a * b); // 5000n
console.log(a / b); // 2n(除法取整)
console.log(a % b); // 0n
console.log(a ** 2n); // 10000n
// 比较运算
console.log(100n > 50n); // true
console.log(100n === 100); // false(类型不同)
console.log(100n == 100); // true(类型转换)
// 位运算
console.log(0b1010n & 0b1100n); // 8n (1000)
console.log(0b1010n | 0b1100n); // 14n (1110)
console.log(0b1010n ^ 0b1100n); // 6n (0110)
// 限制演示
// console.log(100n + 50); // TypeError
console.log(100n + BigInt(50)); // 150n
// console.log(Math.max(100n, 50n)); // TypeError
【代码注释】支持 + - * ** %;除法向零取整。混合 1n + 1 报错,需显式 BigInt(1) 或 Number(1n)(大数可能丢精度)。JSON.stringify 默认不支持 BigInt,需自定义 replacer。
5.5.3 BigInt的实际应用场景
场景一:处理大整数ID
javascript
// 【代码注释】雪花 ID 等超过 MAX_SAFE_INTEGER 时 API 常以字符串传输;前端存 BigInt 或字符串,展示再格式化。与后端约定类型,避免 `Number(id)` 静默舍入。
class IdHandler {
// 处理Twitter Snowflake ID
static parseSnowflakeId(id) {
const snowflake = BigInt(id);
// 提取时间戳(41位)
const timestamp = (snowflake >> 22n) + 1288834974657n;
// 提取数据中心ID(5位)
const datacenterId = (snowflake >> 17n) & 0x1Fn;
// 提取工作机器ID(5位)
const workerId = (snowflake >> 12n) & 0x1Fn;
// 提取序列号(12位)
const sequence = snowflake & 0xFFFn;
return {
timestamp: new Date(Number(timestamp)),
datacenterId: Number(datacenterId),
workerId: Number(workerId),
sequence: Number(sequence)
};
}
static generateSnowflakeId(datacenterId, workerId, sequence) {
const timestamp = BigInt(Date.now()) - 1288834974657n;
return (timestamp << 22n) |
(BigInt(datacenterId) << 17n) |
(BigInt(workerId) << 12n) |
BigInt(sequence);
}
}
// 使用示例
const snowflakeId = '13958471234567890123';
const parsed = IdHandler.parseSnowflakeId(snowflakeId);
console.log(parsed);
const newId = IdHandler.generateSnowflakeId(1, 1, 0);
console.log(newId.toString());
【代码注释】雪花 ID 等超过 MAX_SAFE_INTEGER 时 API 常以字符串传输;前端存 BigInt 或字符串,展示再格式化。与后端约定类型,避免 Number(id) 静默舍入。
场景二:金融计算
javascript
// 【代码注释】以分为单位的 BigInt 相加无浮点误差;展示时 `/100n` 或格式化为元。汇率、利率若需小数,仍可能要用 decimal 库,BigInt 适合整数最小货币单位。
class FinancialCalculator {
// 使用最小单位(分)进行计算
static calculateTotal(prices) {
const total = prices.reduce((sum, price) => {
const cents = BigInt(Math.round(price * 100));
return sum + cents;
}, 0n);
return Number(total) / 100;
}
// 高精度除法
static divide(a, b, precision = 2) {
const multiplier = 10n ** BigInt(precision);
const result = (a * multiplier) / b;
return Number(result) / Math.pow(10, precision);
}
// 复利计算
static compoundInterest(principal, rate, periods) {
const p = BigInt(Math.round(principal * 100));
const r = BigInt(Math.round(rate * 100));
// 使用二项式近似计算
const amount = p * (100n + r) ** BigInt(periods) / (100n ** BigInt(periods));
return Number(amount) / 100;
}
}
// 使用示例
const prices = [10.99, 25.50, 99.95, 4.99];
console.log('总计:', FinancialCalculator.calculateTotal(prices));
// 精确除法
const a = BigInt(100); // 1.00元
const b = BigInt(3); // 3份
console.log('每份:', FinancialCalculator.divide(a, b, 2)); // 0.33元
【代码注释】以分为单位的 BigInt 相加无浮点误差;展示时 /100n 或格式化为元。汇率、利率若需小数,仍可能要用 decimal 库,BigInt 适合整数最小货币单位。
场景三:加密算法
javascript
// 【代码注释】示例用 BigInt 模幂等说明大整数运算;**非生产加密**,真实场景用 Web Crypto API 与标准库。理解 BigInt 取模 `%` 与 number 的差异即可。
class SimpleCrypto {
// RSA相关的大数运算
static modExp(base, exponent, modulus) {
let result = 1n;
base = base % modulus;
while (exponent > 0n) {
if (exponent % 2n === 1n) {
result = (result * base) % modulus;
}
exponent = exponent / 2n;
base = (base * base) % modulus;
}
return result;
}
// 生成大素数(简化版)
static generateLargePrime(bits) {
const getRandomBigint = (bitLength) => {
const bytes = Math.ceil(bitLength / 8);
const array = new Uint8Array(bytes);
crypto.getRandomValues(array);
let result = 0n;
for (const byte of array) {
result = (result << 8n) + BigInt(byte);
}
return result;
};
let candidate = getRandomBigint(bits);
// 实际应用中需要更复杂的素数检测
return candidate | 1n; // 确保是奇数
}
// 简化的哈希函数
static hash(input) {
let hash = 0n;
const str = String(input);
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5n) - hash) + BigInt(char);
hash = hash & 0x7FFFFFFFFFFFFFFFn; // 保持在63位有符号整数范围内
}
return hash;
}
}
// 使用示例
const message = "Hello, World!";
const hashed = SimpleCrypto.hash(message);
console.log('哈希值:', hashed.toString(16));
【代码注释】示例用 BigInt 模幂等说明大整数运算;非生产加密 ,真实场景用 Web Crypto API 与标准库。理解 BigInt 取模 % 与 number 的差异即可。
5.6 数字分隔符
5.6.1 数字分隔符的基本用法
javascript
// 【代码注释】字面量与 `0x`/`0b` 中可用 `_` 分组:`1_000_000`、`0xFF_FF`。仅作视觉分隔,不参与运算。二进制/十六进制按字节/半字节分组更易读。
// 整数分隔
const billion = 1_000_000_000;
const million = 1_000_000;
const thousand = 1_000;
console.log(billion); // 1000000000
// 小数分隔
const pi = 3.141_592_653_589_793;
const avogadro = 6.022_140_76e23;
// 特殊格式的数字
const creditCard = 1234_5678_9012_3456;
const phoneNumber = 138_1234_5678;
const socialSecurity = 123_45_6789;
// 不同进制数字的分隔
const binary = 0b1010_0001_1000_0101;
const octal = 0o123_456;
const hex = 0xA1_B2_C3_D4;
// BigInt的分隔
const largeBigInt = 1_000_000_000_000_000_000_000n;
// 实际应用:常量定义
const CONSTANTS = {
// 物理常数
SPEED_OF_LIGHT: 299_792_458, // m/s
GRAVITATIONAL_CONSTANT: 6.674e-11, // m³/(kg·s²)
// 网络相关
MAX_PORT: 65_535,
LOCALHOST: 127_0_0_1,
// 数据存储
KB: 1_024,
MB: 1_048_576,
GB: 1_073_741_824,
TB: 1_099_511_627_776
};
【代码注释】字面量与 0x/0b 中可用 _ 分组:1_000_000、0xFF_FF。仅作视觉分隔,不参与运算。二进制/十六进制按字节/半字节分组更易读。
5.6.2 数字分隔符的应用场景
场景一:配置文件
javascript
// 【代码注释】大数常量、版本掩码、位标志用分隔符降低抄错位风险;构建工具需支持 ES2021 语法。与注释配合说明每一位含义更佳。
const config = {
// 服务器配置
server: {
port: 8_000,
timeout: 30_000, // 30秒
maxConnections: 1_000
},
// 分页配置
pagination: {
defaultPageSize: 20,
maxPageSize: 100,
totalItems: 10_000
},
// 限流配置
rateLimit: {
requestsPerMinute: 60,
requestsPerHour: 1_000,
banDuration: 3_600_000 // 1小时(毫秒)
},
// 文件上传
upload: {
maxFileSize: 10_485_760, // 10MB
maxTotalSize: 104_857_600, // 100MB
chunkSize: 524_288 // 512KB
}
};
【代码注释】大数常量、版本掩码、位标志用分隔符降低抄错位风险;构建工具需支持 ES2021 语法。与注释配合说明每一位含义更佳。
场景二:数据验证
javascript
// 【代码注释】可用正则允许数字间下划线再 `Number()` 解析,或解析前 `replaceAll('_','')`。银行卡号、许可证号展示常用分隔,存储仍建议规范化无下划线字符串。
class NumberValidator {
// 验证手机号
static validatePhoneNumber(phone) {
const cleanPhone = phone.replaceAll(/[-\s]/g, '');
return /^1[3-9]\d{9}$/.test(cleanPhone);
}
// 验证信用卡号(简化版)
static validateCreditCard(cardNumber) {
const cleanNumber = cardNumber.replaceAll(/[-\s]/g, '');
if (!/^\d{13,19}$/.test(cleanNumber)) {
return false;
}
// Luhn算法验证
let sum = 0;
let isEven = false;
for (let i = cleanNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cleanNumber[i], 10);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
// 验证IP地址
static validateIPAddress(ip) {
const parts = ip.split('.');
if (parts.length !== 4) return false;
return parts.every(part => {
const num = parseInt(part, 10);
return num >= 0 && num <= 255;
});
}
}
// 使用示例
console.log(NumberValidator.validatePhoneNumber('138-1234-5678')); // true
console.log(NumberValidator.validateCreditCard('1234-5678-9012-3456')); // 根据Luhn算法验证
【代码注释】可用正则允许数字间下划线再 Number() 解析,或解析前 replaceAll('_','')。银行卡号、许可证号展示常用分隔,存储仍建议规范化无下划线字符串。
5.7 可选链 ?. 与 空值合并 ??(ES2020)
这两个运算符在 ES2020 引入,与解构赋值高度互补,是现代前端代码中使用频率极高的特性。
5.7.1 可选链运算符 ?.
名词解析:
- 可选链(Optional Chaining) :在访问链式属性时,若某个中间值为
null或undefined,直接短路返回undefined,而不是抛出TypeError
javascript
// 【代码注释】传统写法:每层用 && 短路,冗长且易漏判 null(0、'' 也会被 && 吃掉,语义与 ?. 不同)
const city = user && user.address && user.address.city;
// 【代码注释】?. 仅当左侧为 null/undefined 时短路返回 undefined,不抛 TypeError
const city = user?.address?.city;
// 【代码注释】?.[index]:可选下标访问,tags 不存在时不报错
const firstTag = post?.tags?.[0];
// 【代码注释】?.():仅当左侧存在且为函数时才调用,避免 "x is not a function"
const name = user?.getName?.();
// 【代码注释】深层 API:?. 安全取值 + ?? 提供展示默认值(见下一节)
function renderUserCard(apiData) {
const avatarUrl = apiData?.data?.user?.profile?.avatar?.url ?? '/default-avatar.png';
const displayName = apiData?.data?.user?.name ?? '匿名用户';
const followerCnt = apiData?.data?.user?.stats?.followers ?? 0;
return `
<div class="user-card">
<img src="${avatarUrl}" alt="${displayName}">
<p>${displayName} · ${followerCnt} 粉丝</p>
</div>
`;
}
// 实际应用:事件处理中安全访问目标元素
document.addEventListener('click', (e) => {
// 安全访问 dataset 属性
const userId = e.target?.closest('[data-user-id]')?.dataset?.userId;
if (userId) fetchUserInfo(userId);
});
// 实际应用:配置对象可选字段
function initPlugin(config) {
const timeout = config?.network?.timeout ?? 5000;
const retries = config?.network?.retries ?? 3;
const debugMode = config?.debug ?? false;
console.log(`超时:${timeout}ms 重试:${retries}次 调试:${debugMode}`);
}
【代码注释】?. 短路规则 :左侧为 null/undefined 时立即返回 undefined,不再求值右侧(不访问属性、不调用函数、不取下标)。支持 obj?.prop、obj?.[expr]、fn?.() 三种形式。与 && 区别:0?.x 仍会继续(0 不是 nullish),而 0 && x 会短路。渲染/API/事件委托中常与 ?? 组合:api?.user?.name ?? '访客'。
5.7.2 空值合并运算符 ??
名词解析:
- 空值合并(Nullish Coalescing) :
??仅在左侧为null或undefined时才使用右侧的默认值,0、''、false这类 falsy 值不会触发默认值
javascript
// 【代码注释】?? vs || 的关键差异
// || 逻辑或:任何 falsy 值都会使用右侧(包括 0、''、false)
const port1 = 0 || 3000; // 3000(错误:0 是合法端口,不该被替换)
const name1 = '' || '匿名'; // '匿名'(错误:空字符串可能是用户有意清空)
const flag1 = false || true; // true(错误:false 是有意义的布尔值)
// ?? 空值合并:只有 null/undefined 才使用默认值
const port2 = 0 ?? 3000; // 0 ✅ 保留合法的 0
const name2 = '' ?? '匿名'; // '' ✅ 保留空字符串
const flag2 = false ?? true; // false ✅ 保留 false
// 实际应用:表单字段默认值处理
function processFormField(value) {
const displayValue = value ?? '(未填写)'; // 只对 null/undefined 使用默认值
return displayValue;
}
console.log(processFormField(0)); // 0(数量0是有效值)
console.log(processFormField('')); // ''(空字符串是有效值)
console.log(processFormField(null)); // '(未填写)'
console.log(processFormField(undefined)); // '(未填写)'
// 实际应用:配置项合并(与解构默认值的对比)
function createConfig(options) {
return {
// ?? 与解构默认值等效,但 ?? 可用于运行时动态合并
host: options.host ?? 'localhost',
port: options.port ?? 8080, // port=0 也能正确保留
debug: options.debug ?? false, // debug=false 也能正确保留
maxConn: options.maxConn ?? 100
};
}
console.log(createConfig({ port: 0, debug: false }));
// { host: 'localhost', port: 0, debug: false, maxConn: 100 }
// 【代码注释】?. 与 ?? 的黄金组合:安全读取 + 优雅回退
class UserService {
getDisplayInfo(user) {
return {
name: user?.profile?.displayName ?? user?.name ?? '匿名',
avatar: user?.profile?.avatarUrl ?? '/assets/default-avatar.png',
bio: user?.profile?.bio ?? '',
verified: user?.verification?.isVerified ?? false
};
}
}
【代码注释】?? 的 nullish 语义:仅当左侧为 null 或 undefined 才取右侧默认值;0、''、false 均为合法业务值,不会被替换。对比 ||:会把所有 falsy 都换掉,导致端口 0、开关 false、用户清空的 '' 被误覆盖。解构默认值 function f({ port = 8080 }) 与 port ?? 8080 类似,但 ?? 可在运行时对任意表达式使用。UserService.getDisplayInfo 演示多层 ?. 与多级 ?? 回退链。
#mermaid-svg-52A7mFtgTlXKqipK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-52A7mFtgTlXKqipK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-52A7mFtgTlXKqipK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-52A7mFtgTlXKqipK .error-icon{fill:#552222;}#mermaid-svg-52A7mFtgTlXKqipK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-52A7mFtgTlXKqipK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-52A7mFtgTlXKqipK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-52A7mFtgTlXKqipK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-52A7mFtgTlXKqipK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-52A7mFtgTlXKqipK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-52A7mFtgTlXKqipK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-52A7mFtgTlXKqipK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-52A7mFtgTlXKqipK .marker.cross{stroke:#333333;}#mermaid-svg-52A7mFtgTlXKqipK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-52A7mFtgTlXKqipK p{margin:0;}#mermaid-svg-52A7mFtgTlXKqipK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-52A7mFtgTlXKqipK .cluster-label text{fill:#333;}#mermaid-svg-52A7mFtgTlXKqipK .cluster-label span{color:#333;}#mermaid-svg-52A7mFtgTlXKqipK .cluster-label span p{background-color:transparent;}#mermaid-svg-52A7mFtgTlXKqipK .label text,#mermaid-svg-52A7mFtgTlXKqipK span{fill:#333;color:#333;}#mermaid-svg-52A7mFtgTlXKqipK .node rect,#mermaid-svg-52A7mFtgTlXKqipK .node circle,#mermaid-svg-52A7mFtgTlXKqipK .node ellipse,#mermaid-svg-52A7mFtgTlXKqipK .node polygon,#mermaid-svg-52A7mFtgTlXKqipK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-52A7mFtgTlXKqipK .rough-node .label text,#mermaid-svg-52A7mFtgTlXKqipK .node .label text,#mermaid-svg-52A7mFtgTlXKqipK .image-shape .label,#mermaid-svg-52A7mFtgTlXKqipK .icon-shape .label{text-anchor:middle;}#mermaid-svg-52A7mFtgTlXKqipK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-52A7mFtgTlXKqipK .rough-node .label,#mermaid-svg-52A7mFtgTlXKqipK .node .label,#mermaid-svg-52A7mFtgTlXKqipK .image-shape .label,#mermaid-svg-52A7mFtgTlXKqipK .icon-shape .label{text-align:center;}#mermaid-svg-52A7mFtgTlXKqipK .node.clickable{cursor:pointer;}#mermaid-svg-52A7mFtgTlXKqipK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-52A7mFtgTlXKqipK .arrowheadPath{fill:#333333;}#mermaid-svg-52A7mFtgTlXKqipK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-52A7mFtgTlXKqipK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-52A7mFtgTlXKqipK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-52A7mFtgTlXKqipK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-52A7mFtgTlXKqipK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-52A7mFtgTlXKqipK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-52A7mFtgTlXKqipK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-52A7mFtgTlXKqipK .cluster text{fill:#333;}#mermaid-svg-52A7mFtgTlXKqipK .cluster span{color:#333;}#mermaid-svg-52A7mFtgTlXKqipK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-52A7mFtgTlXKqipK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-52A7mFtgTlXKqipK rect.text{fill:none;stroke-width:0;}#mermaid-svg-52A7mFtgTlXKqipK .icon-shape,#mermaid-svg-52A7mFtgTlXKqipK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-52A7mFtgTlXKqipK .icon-shape p,#mermaid-svg-52A7mFtgTlXKqipK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-52A7mFtgTlXKqipK .icon-shape .label rect,#mermaid-svg-52A7mFtgTlXKqipK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-52A7mFtgTlXKqipK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-52A7mFtgTlXKqipK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-52A7mFtgTlXKqipK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有中间 null/undefined
链路完整
是
否,含0/false/''
访问深层属性
?.短路返回 undefined
返回目标值
是 null/undefined?
?? 提供默认值
使用原值
【代码注释】决策流:访问深层字段 → 中间层可能为空则用 ?. 短路,避免 Cannot read properties of undefined;需要兜底展示/配置 → 在 ?. 结果后用 ?? 补默认值。勿混用 || 做默认值(会误伤 0/false/'')。与解构默认值、逻辑与 && 的分工:解构适合形参/已知结构;?./?? 适合运行时 API 形状不确定的场景。
5.7.3 逻辑赋值运算符(ES2021)
ES2021 引入了三个结合逻辑判断与赋值 的复合运算符:??=、||=、&&=。它们是 ??/||/&& 与 = 的结合体,在左侧满足条件时才执行赋值(具有短路语义)。
javascript
// ──────────────────────────────────────────────────────────
// 【代码注释】??= 空值合并赋值:仅当左侧为 null / undefined 时才赋新值
// 语义等价:a ?? (a = b) → 只有 nullish 才触发,0 / '' / false 不会被覆盖
// ──────────────────────────────────────────────────────────
let userTheme = null;
userTheme ??= 'light'; // null → 赋值
console.log(userTheme); // 'light'
let port = 0;
port ??= 3000; // 0 不是 nullish → 不赋值,保留合法的 0
console.log(port); // 0 ✅
// 对比 ||= 的风险:
let port2 = 0;
port2 ||= 3000; // 0 是 falsy → 赋值(❗ 覆盖了合法端口 0)
console.log(port2); // 3000 ← 通常不是期望行为
// ──────────────────────────────────────────────────────────
// 【代码注释】||= 逻辑或赋值:左侧为 falsy(含 0/false/'')时才赋值
// 适合:字符串显示默认值、fallback 文案(不需要区分 null 与 '')
// ──────────────────────────────────────────────────────────
let displayName = '';
displayName ||= '匿名用户'; // '' 是 falsy → 赋值
console.log(displayName); // '匿名用户'
let retries = undefined;
retries ||= 3; // undefined 是 falsy → 赋值
console.log(retries); // 3
// ──────────────────────────────────────────────────────────
// 【代码注释】&&= 逻辑与赋值:左侧为 truthy 时才赋值(条件性更新)
// 适合:仅当属性存在时才更新它;若属性为 falsy,跳过赋值(不触发 setter)
// ──────────────────────────────────────────────────────────
let user = { name: '张三', isVerified: true };
user.isVerified &&= false; // isVerified 为 truthy → 执行赋值
console.log(user.isVerified); // false
let guest = { name: '游客', isVerified: false };
guest.isVerified &&= false; // isVerified 为 falsy → 不执行赋值(无副作用)
console.log(guest.isVerified); // false(未触发,避免不必要的 setter)
【代码注释】三个运算符的核心差异:??= 仅对 null/undefined (最严格);||= 对所有 falsy (含 0/''/false);&&= 在 truthy 时才赋值。与普通 = 不同的是,三者都具有短路语义------左侧不满足条件时右侧不求值,也不触发 setter,性能和语义均更精确。
javascript
// ──────────────────────────────────────────────────────────
// 实际应用场景集合
// ──────────────────────────────────────────────────────────
// 【代码注释】场景一:懒初始化(Lazy Initialization)------??= 是最常见用法
class ComponentState {
#cache = null;
#listeners = null;
// 首次调用时初始化 Map,后续复用同一实例
getCache() {
this.#cache ??= new Map();
return this.#cache;
}
// 首次添加监听器时初始化数组
addListener(fn) {
this.#listeners ??= [];
this.#listeners.push(fn);
}
}
// 【代码注释】场景二:配置项安全合并------??= 保留合法的 0 / false / ''
function withDefaults(options) {
options.timeout ??= 5_000; // 仅补充缺失项,timeout=0 也会被保留
options.retries ??= 3;
options.debug ??= false; // debug=false 也会被保留(不被 ||= 误覆盖)
return options;
}
console.log(withDefaults({ timeout: 0, debug: true }));
// { timeout: 0, retries: 3, debug: true } ← timeout=0 正确保留
// 【代码注释】场景三:条件性更新对象属性------&&= 不触发不必要的 setter
function updateUserStatus(user, shouldDeactivate) {
// 仅当 isActive 为 true 且需要停用时才修改(&&= 短路,不触发 Vue/MobX 响应式)
if (shouldDeactivate) {
user.isActive &&= false;
}
}
// 【代码注释】场景四:||= 用于展示层默认文案(不需区分 null 与空字符串时)
function renderUserBio(bio) {
let displayBio = bio;
displayBio ||= '这个人很懒,什么都没有写...'; // null / undefined / '' 均使用默认文案
return displayBio;
}
【代码注释】选型建议:配置/初始化 优先用 ??=(最安全,不误伤 0/false/'');展示文案 可用 ||=(把空字符串也视为"未填写");条件性更新 用 &&=(避免不必要的 setter 触发)。在 Vue 3 响应式对象、MobX observable 上,&&= 的短路特性可避免无意义的派发(dispatch),有利于性能。
渲染错误: Mermaid 渲染失败: Parse error on line 3: ...-->|展示场景,'' 也算空| C\|\|= 展示层 A -->|仅在 -----------------------^ Expecting 'NODE_STRING', got 'PIPE'
【代码注释】??= / ||= / &&= 决策流:优先考虑「0/false/'' 是否是合法值」------ 是则用 ??=;纯展示降级可用 ||=;需要「值存在才更新」用 &&=。三者均支持链式属性 obj.prop ??= default,行为与 obj.prop = obj.prop ?? default 等价但更简洁,且短路时不触发 getter/setter。
6. 实战应用场景与最佳实践
6.1 现代前端开发中的ES6+应用
6.1.1 React组件开发
javascript
// 【代码注释】函数组件 + 解构 props/state + 模板字符串 className + const 事件处理;hooks 依赖块级 let/const。对比 class 组件,现代代码默认严格模式与模块作用域。
import React, { useState, useEffect, useCallback } from 'react';
// 使用箭头函数和解构
const UserProfile = ({ userId, onUpdate }) => {
// 使用解构和默认值
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// 使用async/await和模板字符串
useEffect(() => {
const fetchUser = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
// 使用useCallback和模板字符串
const handleUpdate = useCallback((updates) => {
const updatedUser = { ...user, ...updates };
setUser(updatedUser);
onUpdate(updatedUser);
}, [user, onUpdate]);
// 使用条件渲染和模板字符串
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
return (
<div className="user-profile">
{/* 使用模板字符串 */}
<h1>{`User: ${user.name}`}</h1>
<p>Email: {user.email}</p>
<p>Age: {user.age}</p>
{/* 使用解构 */}
{user.address && (
<address>
{user.address.street}<br />
{user.address.city}, {user.address.country}
</address>
)}
<button onClick={() => handleUpdate({ name: 'Updated Name' })}>
Update Name
</button>
</div>
);
};
export default UserProfile;
【代码注释】函数组件 + 解构 props/state + 模板字符串 className + const 事件处理;hooks 依赖块级 let/const。对比 class 组件,现代代码默认严格模式与模块作用域。
6.1.2 Vue3 Composition API
javascript
// 【代码注释】`<script setup>` 中 `const props = defineProps`、解构 `toRefs`、模板 `${}` 与计算属性;与 React 同属「声明式 + 块级变量」范式。
import { ref, computed, onMounted, watch } from 'vue';
export default {
props: {
userId: {
type: Number,
required: true,
validator: value => value > 0
}
},
setup(props, { emit }) {
// 使用ref解构
const user = ref(null);
const loading = ref(false);
const error = ref(null);
// 使用computed
const userDisplayName = computed(() => {
if (!user.value) return '';
const { firstName, lastName } = user.value;
return `${firstName} ${lastName}`;
});
// 使用async/await
const fetchUser = async () => {
loading.value = true;
error.value = null;
try {
const response = await fetch(`/api/users/${props.userId}`);
const userData = await response.json();
user.value = userData;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
// 使用watch
watch(() => props.userId, (newUserId, oldUserId) => {
if (newUserId !== oldUserId) {
fetchUser();
}
});
// 使用解构
const updateUser = (updates) => {
user.value = { ...user.value, ...updates };
emit('update:user', user.value);
};
// 使用onMounted
onMounted(() => {
fetchUser();
});
// 返回解构的对象
return {
user,
loading,
error,
userDisplayName,
updateUser
};
}
};
【代码注释】<script setup> 中 const props = defineProps、解构 toRefs、模板 ${} 与计算属性;与 React 同属「声明式 + 块级变量」范式。
6.1.3 Node.js后端开发
javascript
// 【代码注释】路由处理函数参数解构 `req.body`、默认配置 const、模板字符串拼错误信息。Node 模块默认严格模式(ESM)或 CommonJS 中手动 strict。
import express from 'express';
import { body, validationResult, param } from 'express-validator';
const router = express.Router();
// 使用解构和async/await
router.get('/users/:id',
param('id').isInt({ min: 1 }),
async (req, res, next) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { id } = req.params;
const { include, fields } = req.query;
// 使用解构和模板字符串
const user = await User.findById(id);
if (!user) {
return res.status(404).json({
error: `User with id ${id} not found`
});
}
// 使用可选链和空值合并
const response = {
id: user.id,
name: user.name,
email: user.email,
profile: user.profile ?? null
};
// 处理include参数
if (include) {
const includes = include.split(',');
if (includes.includes('posts')) {
response.posts = await user.getPosts();
}
if (includes.includes('comments')) {
response.comments = await user.getComments();
}
}
res.json(response);
} catch (error) {
next(error);
}
}
);
// 使用解构和验证
router.post('/users',
[
body('name').trim().isLength({ min: 2, max: 50 }),
body('email').isEmail().normalizeEmail(),
body('age').optional().isInt({ min: 18, max: 120 })
],
async (req, res, next) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, age } = req.body;
// 使用展开运算符
const user = await User.create({
name,
email,
age: age ?? 18,
createdAt: new Date(),
updatedAt: new Date()
});
res.status(201).json(user);
} catch (error) {
next(error);
}
}
);
export default router;
【代码注释】路由处理函数参数解构 req.body、默认配置 const、模板字符串拼错误信息。Node 模块默认严格模式(ESM)或 CommonJS 中手动 strict。
6.2 工具函数库开发
6.2.1 字符串处理工具
javascript
// 【代码注释】封装校验、格式化、slug 生成等纯函数,便于单测。结合 `includes`/`replaceAll`/`padStart` 覆盖常见表单与展示需求。
class StringUtils {
// 驼峰命名转换
static toCamelCase(str) {
return str
.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
.replace(/^(.)/, (char) => char.toLowerCase());
}
// 短横线命名转换
static toKebabCase(str) {
return str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/[\s_]+/g, '-')
.toLowerCase();
}
// 蛇形命名转换
static toSnakeCase(str) {
return str
.replace(/([a-z])([A-Z])/g, '$1_$2')
.replace(/[\s-]+/g, '_')
.toLowerCase();
}
// 生成唯一ID
static generateId(prefix = 'id') {
const timestamp = Date.now().toString(36);
const randomStr = Math.random().toString(36).substring(2, 8);
return `${prefix}_${timestamp}_${randomStr}`;
}
// 截断文本
static truncate(str, maxLength, suffix = '...') {
if (str.length <= maxLength) return str;
return str.substring(0, maxLength - suffix.length) + suffix;
}
// 移除HTML标签
static stripHtml(html) {
const tmp = document.createElement('div');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || '';
}
// 高亮关键词
static highlightKeywords(text, keywords, className = 'highlight') {
let result = text;
keywords.forEach(keyword => {
const regex = new RegExp(`(${keyword})`, 'gi');
result = result.replace(regex, `<span class="${className}">$1</span>`);
});
return result;
}
}
// 使用示例
console.log(StringUtils.toCamelCase('hello-world-example')); // helloWorldExample
console.log(StringUtils.toKebabCase('helloWorldExample')); // hello-world-example
console.log(StringUtils.generateId('user')); // user_xxxxx_xxxxxx
【代码注释】封装校验、格式化、slug 生成等纯函数,便于单测。结合 includes/replaceAll/padStart 覆盖常见表单与展示需求。
6.2.2 数组处理工具
javascript
// 【代码注释】解构、`...spread`、 `Array.from` 处理类数组与 immutable 更新模式。注意浅拷贝:嵌套对象仍需深拷贝或 Immer。
class ArrayUtils {
// 数组去重
static unique(arr, key = null) {
if (key) {
const seen = new Set();
return arr.filter(item => {
const keyValue = item[key];
if (seen.has(keyValue)) {
return false;
}
seen.add(keyValue);
return true;
});
}
return [...new Set(arr)];
}
// 数组分组
static groupBy(arr, key) {
return arr.reduce((result, item) => {
const groupKey = item[key];
if (!result[groupKey]) {
result[groupKey] = [];
}
result[groupKey].push(item);
return result;
}, {});
}
// 数组分块
static chunk(arr, size) {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
// 数组排序
static sortBy(arr, key, order = 'asc') {
return [...arr].sort((a, b) => {
const aVal = a[key];
const bVal = b[key];
if (aVal < bVal) return order === 'asc' ? -1 : 1;
if (aVal > bVal) return order === 'asc' ? 1 : -1;
return 0;
});
}
// 数组随机
static shuffle(arr) {
const result = [...arr];
for (let i = result.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
// 数组差集
static difference(arr1, arr2) {
const set2 = new Set(arr2);
return arr1.filter(item => !set2.has(item));
}
// 数组交集
static intersection(arr1, arr2) {
const set2 = new Set(arr2);
return arr1.filter(item => set2.has(item));
}
}
// 使用示例
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 25 },
{ id: 4, name: '赵六', age: 30 }
];
console.log(ArrayUtils.groupBy(users, 'age'));
console.log(ArrayUtils.chunk([1, 2, 3, 4, 5], 2));
console.log(ArrayUtils.sortBy(users, 'age'));
【代码注释】解构、...spread、 Array.from 处理类数组与 immutable 更新模式。注意浅拷贝:嵌套对象仍需深拷贝或 Immer。
6.3 性能优化技巧
6.3.1 内存优化
javascript
// 【代码注释】块级变量及时失效利于 GC;避免闭包长期持有大对象。`const` 引用不变但对象过大时仍占内存;列表渲染用 key 与虚拟滚动而非全局 var。
class MemoryOptimizer {
// 使用WeakMap避免内存泄漏
static createCache() {
const cache = new WeakMap();
return {
get(key) {
return cache.get(key);
},
set(key, value) {
cache.set(key, value);
return this;
},
has(key) {
return cache.has(key);
},
delete(key) {
return cache.delete(key);
}
};
}
// 使用WeakRef处理大对象
static processLargeObject(obj) {
const weakRef = new WeakRef(obj);
// 模拟异步处理
setTimeout(() => {
const result = weakRef.deref();
if (result) {
console.log('处理结果:', result);
} else {
console.log('对象已被回收');
}
}, 1000);
}
// 使用FinalizationRegistry清理资源
static createResourceRegistry() {
const registry = new FinalizationRegistry((heldValue) => {
console.log('清理资源:', heldValue);
// 清理逻辑
});
return {
register(obj, resource) {
registry.register(obj, resource, obj);
},
unregister(obj) {
registry.unregister(obj);
}
};
}
}
// 使用示例
const cache = MemoryOptimizer.createCache();
const key = { id: 1 };
cache.set(key, { data: '重要数据' });
【代码注释】块级变量及时失效利于 GC;避免闭包长期持有大对象。const 引用不变但对象过大时仍占内存;列表渲染用 key 与虚拟滚动而非全局 var。
6.3.2 异步操作优化
javascript
// 【代码注释】Promise.all 与 let 块级绑定、解构取结果项组合;避免在循环里 `await` 串行除非必要。错误处理用 try/catch 或 `.catch`,解构 `{ success, result }` 统一返回形状。
class AsyncOptimizer {
// 并发控制
static async concurrentLimit(tasks, limit = 3) {
const results = [];
const executing = [];
for (const task of tasks) {
const promise = task().then(result => {
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
// 重试机制
static async retry(fn, options = {}) {
const {
times = 3,
delay = 1000,
backoff = 2,
condition = () => true
} = options;
let lastError;
for (let i = 0; i < times; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (i < times - 1 && condition(error)) {
const waitTime = delay * Math.pow(backoff, i);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
}
throw lastError;
}
// 超时控制
static async timeout(promise, ms, message = '操作超时') {
const timer = new Promise((_, reject) => {
setTimeout(() => reject(new Error(message)), ms);
});
return Promise.race([promise, timer]);
}
}
// 使用示例
const tasks = [
() => fetch('/api/data1'),
() => fetch('/api/data2'),
() => fetch('/api/data3'),
() => fetch('/api/data4'),
() => fetch('/api/data5')
];
AsyncOptimizer.concurrentLimit(tasks, 2).then(results => {
console.log('所有任务完成:', results);
});
【代码注释】Promise.all 与 let 块级绑定、解构取结果项组合;避免在循环里 await 串行除非必要。错误处理用 try/catch 或 .catch,解构 { success, result } 统一返回形状。
7. 知识点总结与对比
7.1 ES5 vs ES6+ 特性对比表
| 特性分类 | ES5 | ES6+ | 优势 |
|---|---|---|---|
| 变量声明 | var | let, const | 块级作用域、TDZ 强制先声明 |
| 字符串 | 单引号/双引号 | 模板字符串、String.raw | 支持换行、变量插值、原始字符串 |
| 字符串访问 | str[str.length-1] |
str.at(-1) |
支持负索引,更简洁 |
| 函数 | function关键字 | 箭头函数 | 简洁语法、this绑定 |
| 参数 | 默认参数缺失 | 默认参数、剩余参数 | 更灵活的参数处理 |
| 对象 | 对象字面量 | 解构、展开运算符 | 简化对象操作 |
| 数组 | Array方法 | 解构、展开运算符 | 简化数组操作 |
| 属性访问 | a && a.b && a.b.c |
a?.b?.c |
可选链防 TypeError |
| 默认值 | `val | default` | |
| 赋值默认值 | `a = a | b`(误伤 falsy) | |
| 对象不可变 | Object.freeze(obj) |
const + deepFreeze |
深度冻结配置/枚举常量 |
| 异步 | 回调函数 | Promise、async/await | 更优雅的异步处理 |
| 类 | 原型链 | class关键字 | 更直观的面向对象编程 |
| 模块 | 无模块系统 | import/export | 原生模块支持 |
| 数据类型 | 5种基本类型 | Symbol、BigInt | 更丰富的数据类型 |
| 浮点精度 | 手动误差判断 | Number.EPSILON | 标准化误差常量 |
7.2 使用场景对比
#mermaid-svg-OkC0F2mBylUMvtYT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OkC0F2mBylUMvtYT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OkC0F2mBylUMvtYT .error-icon{fill:#552222;}#mermaid-svg-OkC0F2mBylUMvtYT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OkC0F2mBylUMvtYT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OkC0F2mBylUMvtYT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OkC0F2mBylUMvtYT .marker.cross{stroke:#333333;}#mermaid-svg-OkC0F2mBylUMvtYT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OkC0F2mBylUMvtYT p{margin:0;}#mermaid-svg-OkC0F2mBylUMvtYT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OkC0F2mBylUMvtYT .cluster-label text{fill:#333;}#mermaid-svg-OkC0F2mBylUMvtYT .cluster-label span{color:#333;}#mermaid-svg-OkC0F2mBylUMvtYT .cluster-label span p{background-color:transparent;}#mermaid-svg-OkC0F2mBylUMvtYT .label text,#mermaid-svg-OkC0F2mBylUMvtYT span{fill:#333;color:#333;}#mermaid-svg-OkC0F2mBylUMvtYT .node rect,#mermaid-svg-OkC0F2mBylUMvtYT .node circle,#mermaid-svg-OkC0F2mBylUMvtYT .node ellipse,#mermaid-svg-OkC0F2mBylUMvtYT .node polygon,#mermaid-svg-OkC0F2mBylUMvtYT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OkC0F2mBylUMvtYT .rough-node .label text,#mermaid-svg-OkC0F2mBylUMvtYT .node .label text,#mermaid-svg-OkC0F2mBylUMvtYT .image-shape .label,#mermaid-svg-OkC0F2mBylUMvtYT .icon-shape .label{text-anchor:middle;}#mermaid-svg-OkC0F2mBylUMvtYT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OkC0F2mBylUMvtYT .rough-node .label,#mermaid-svg-OkC0F2mBylUMvtYT .node .label,#mermaid-svg-OkC0F2mBylUMvtYT .image-shape .label,#mermaid-svg-OkC0F2mBylUMvtYT .icon-shape .label{text-align:center;}#mermaid-svg-OkC0F2mBylUMvtYT .node.clickable{cursor:pointer;}#mermaid-svg-OkC0F2mBylUMvtYT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OkC0F2mBylUMvtYT .arrowheadPath{fill:#333333;}#mermaid-svg-OkC0F2mBylUMvtYT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OkC0F2mBylUMvtYT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OkC0F2mBylUMvtYT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OkC0F2mBylUMvtYT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OkC0F2mBylUMvtYT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OkC0F2mBylUMvtYT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OkC0F2mBylUMvtYT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OkC0F2mBylUMvtYT .cluster text{fill:#333;}#mermaid-svg-OkC0F2mBylUMvtYT .cluster span{color:#333;}#mermaid-svg-OkC0F2mBylUMvtYT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OkC0F2mBylUMvtYT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OkC0F2mBylUMvtYT rect.text{fill:none;stroke-width:0;}#mermaid-svg-OkC0F2mBylUMvtYT .icon-shape,#mermaid-svg-OkC0F2mBylUMvtYT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OkC0F2mBylUMvtYT .icon-shape p,#mermaid-svg-OkC0F2mBylUMvtYT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OkC0F2mBylUMvtYT .icon-shape .label rect,#mermaid-svg-OkC0F2mBylUMvtYT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OkC0F2mBylUMvtYT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OkC0F2mBylUMvtYT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OkC0F2mBylUMvtYT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ES6+特性选择指南
变量声明
函数定义
异步处理
数据操作
const: 配置/常量
let: 可变变量
避免使用var
箭头函数: 回调/短函数
传统函数: 方法/构造函数
Promise: 链式异步
async/await: 顺序异步
解构: 数据提取
展开: 数组合并
模板字符串: 文本拼接
【代码注释】对照表从可读性、安全性、性能、兼容性四维给建议:新代码默认 let/const;多字段提取用解构;拼接用模板字符串;遗留环境走 Babel。决策树比死记语法更有长期价值。
7.3 最佳实践建议
7.3.1 变量声明规范
javascript
// 【代码注释】默认 const,需要重新赋值再 let;禁止新代码 var。循环索引、累加器 let;配置与 import const。启用 ESLint `prefer-const`、`no-var`。
// ✅ 推荐:优先使用const
const CONFIG = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
// ✅ 推荐:需要在循环中重新赋值时使用let
for (let i = 0; i < 10; i++) {
console.log(i);
}
// ❌ 避免:不要使用var(除非有特殊需求)
var oldStyle = '避免使用';
// ✅ 推荐:在块级作用域中使用let/const
function processData(data) {
const result = [];
for (const item of data) {
let processed = transform(item);
result.push(processed);
}
return result;
}
【代码注释】默认 const,需要重新赋值再 let;禁止新代码 var。循环索引、累加器 let;配置与 import const。启用 ESLint prefer-const、no-var。
7.3.2 函数定义规范
javascript
// 【代码注释】优先箭头函数作回调(不绑定 this);方法简写与解构参数减少样板代码。需要 `arguments` 对象或构造函数时用 function 声明。
// ✅ 推荐:简短回调使用箭头函数
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
// ✅ 推荐:对象方法使用传统函数
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
// ✅ 推荐:复杂逻辑使用传统函数
function processUserData(userData) {
// 复杂的处理逻辑
let result = {};
// 多行处理
for (const [key, value] of Object.entries(userData)) {
// ...
}
return result;
}
// ❌ 避免:不要在需要this上下文的地方使用箭头函数
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log(this); // this不会指向button
});
【代码注释】优先箭头函数作回调(不绑定 this);方法简写与解构参数减少样板代码。需要 arguments 对象或构造函数时用 function 声明。
7.3.3 异步处理规范
javascript
// 【代码注释】async/await 配合 try/catch;并发用 `Promise.all`/`allSettled`。解构 API 响应固定字段;错误对象 `{ code, message }` 统一解构处理。
// ✅ 推荐:使用async/await处理顺序异步
async function fetchUserData(userId) {
try {
const user = await fetch(`/api/users/${userId}`);
const posts = await fetch(`/api/users/${userId}/posts`);
const comments = await fetch(`/api/users/${userId}/comments`);
return {
user: await user.json(),
posts: await posts.json(),
comments: await comments.json()
};
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// ✅ 推荐:使用Promise.all处理并行异步
async function fetchAllUserIds() {
try {
const response = await fetch('/api/users');
const users = await response.json();
const userDetails = await Promise.all(
users.map(user => fetch(`/api/users/${user.id}`))
);
return await Promise.all(userDetails.map(res => res.json()));
} catch (error) {
console.error('获取用户详情失败:', error);
throw error;
}
}
// ✅ 推荐:错误处理要全面
async function safeApiCall(url, options = {}) {
try {
const response = await fetch(url, {
timeout: 5000,
...options
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.error('请求超时');
} else if (error instanceof TypeError) {
console.error('网络错误');
} else {
console.error('未知错误:', error);
}
throw error;
}
}
【代码注释】async/await 配合 try/catch;并发用 Promise.all/allSettled。解构 API 响应固定字段;错误对象 { code, message } 统一解构处理。
7.4 性能对比与优化
7.4.1 性能对比
javascript
// 【代码注释】微观基准中模板字符串与 `+` 差异通常可忽略;解构有轻微赋值成本但换来可维护性。热点路径再 profile,勿过早优化。
class PerformanceComparison {
// 字符串拼接性能
static stringConcatenation() {
const iterations = 100000;
let result = '';
// 传统方式(慢)
console.time('传统字符串拼接');
for (let i = 0; i < iterations; i++) {
result += 'item';
}
console.timeEnd('传统字符串拼接');
// 数组join(快)
console.time('数组join');
const items = [];
for (let i = 0; i < iterations; i++) {
items.push('item');
}
result = items.join('');
console.timeEnd('数组join');
// 模板字符串(中等)
console.time('模板字符串');
result = '';
for (let i = 0; i < iterations; i++) {
result = `${result}item`;
}
console.timeEnd('模板字符串');
}
// 数组遍历性能
static arrayIteration() {
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
// 传统for循环(最快)
console.time('传统for循环');
let sum = 0;
for (let i = 0; i < largeArray.length; i++) {
sum += largeArray[i];
}
console.timeEnd('传统for循环');
// forEach(中等)
console.time('forEach');
sum = 0;
largeArray.forEach(item => {
sum += item;
});
console.timeEnd('forEach');
// reduce(较慢)
console.time('reduce');
sum = largeArray.reduce((acc, item) => acc + item, 0);
console.timeEnd('reduce');
// for...of(慢)
console.time('for...of');
sum = 0;
for (const item of largeArray) {
sum += item;
}
console.timeEnd('for...of');
}
}
【代码注释】微观基准中模板字符串与 + 差异通常可忽略;解构有轻微赋值成本但换来可维护性。热点路径再 profile,勿过早优化。
7.5 兼容性处理
7.5.1 转译和Polyfill
javascript
// 【代码注释】Babel 转译语法(let/class/解构);core-js 等 polyfill 补 API(Promise、Object.assign)。按 browserslist 决定垫片范围;`<script type="module">` 天然严格模式。
// 使用Babel转译
// 转译前
const arrowFunction = () => {
console.log('箭头函数');
};
// 转译后
var arrowFunction = function arrowFunction() {
console.log('箭头函数');
};
// 使用Polyfill
// 检测并添加缺失的方法
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
// 现代化的兼容性检测
class FeatureDetection {
static supportsES6() {
try {
// 检测箭头函数
eval('const arrow = () => true');
// 检测let/const
eval('let x = 1');
eval('const y = 1');
// 检测解构
eval('const {a} = {a: 1}');
eval('const [b] = [1]');
// 检测模板字符串
eval('const template = `test`');
return true;
} catch (e) {
return false;
}
}
static getBrowserCompatibility() {
return {
arrowFunctions: this.supportsES6(),
templateLiterals: this.supportsES6(),
destructuring: this.supportsES6(),
promises: typeof Promise !== 'undefined',
map: typeof Map !== 'undefined',
set: typeof Set !== 'undefined',
weakMap: typeof WeakMap !== 'undefined',
weakSet: typeof WeakSet !== 'undefined',
symbol: typeof Symbol !== 'undefined',
proxy: typeof Proxy !== 'undefined'
};
}
}
【代码注释】Babel 转译语法(let/class/解构);core-js 等 polyfill 补 API(Promise、Object.assign)。按 browserslist 决定垫片范围;<script type="module"> 天然严格模式。
8. Day01 知识点速查与归纳
8.1 一句话记忆卡
| 模块 | 核心要点 |
|---|---|
| 严格模式 | 'use strict' 消灭隐式全局、非法字面量等静默错误 |
| let | 不提升(TDZ)、不重复声明、块级作用域、非 window 属性 |
| TDZ | let/const 提升后标记 <uninitialized>,访问即 ReferenceError |
| const | 同 let 作用域规则 + 绑定不可重新赋值 |
| 数组解构 | 按索引匹配;可交换变量、函数形参、解构伪数组 |
| 对象解构 | 按属性名匹配;可重命名、默认值、嵌套 |
| for...of + 解构 | 遍历 Map/Object.entries 时同时拆包 [key, value] |
| 模板字符串 | 反引号 + 换行 + ${表达式};标签模板用于 XSS 防护、i18n |
| 字符串方法 | 查找用 includes;对齐用 pad;清洗用 trim;新 at(-1) 负索引 |
| String.raw | 内置标签函数,返回不处理转义的原始字符串(Windows 路径必备) |
| 数值 | 0b/0o 字面量、**、BigInt、数字分隔符 _ |
| 浮点精度 | 0.1+0.2 ≠ 0.3;用 Number.EPSILON 或"转分运算"解决 |
可选链 ?. |
链式访问中遇 null/undefined 短路返回 undefined,防 TypeError |
空值合并 ?? |
仅 null/undefined 触发默认值,0/''/false 不受影响 |
逻辑赋值 ??= |
仅 nullish 时才赋值(最安全的懒初始化 / 配置合并方式) |
| 逻辑赋值 ` | |
逻辑赋值 &&= |
truthy 时才赋值,条件更新对象属性且不触发不必要的 setter |
| Object.freeze | 冻结对象顶层属性;配合 deepFreeze 实现枚举常量/配置深度不可变 |
8.2 Day01 知识关系总图
#mermaid-svg-3mb4HAxEb9iD3o0W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3mb4HAxEb9iD3o0W .error-icon{fill:#552222;}#mermaid-svg-3mb4HAxEb9iD3o0W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3mb4HAxEb9iD3o0W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3mb4HAxEb9iD3o0W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3mb4HAxEb9iD3o0W .marker.cross{stroke:#333333;}#mermaid-svg-3mb4HAxEb9iD3o0W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3mb4HAxEb9iD3o0W p{margin:0;}#mermaid-svg-3mb4HAxEb9iD3o0W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster-label text{fill:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster-label span{color:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster-label span p{background-color:transparent;}#mermaid-svg-3mb4HAxEb9iD3o0W .label text,#mermaid-svg-3mb4HAxEb9iD3o0W span{fill:#333;color:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W .node rect,#mermaid-svg-3mb4HAxEb9iD3o0W .node circle,#mermaid-svg-3mb4HAxEb9iD3o0W .node ellipse,#mermaid-svg-3mb4HAxEb9iD3o0W .node polygon,#mermaid-svg-3mb4HAxEb9iD3o0W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3mb4HAxEb9iD3o0W .rough-node .label text,#mermaid-svg-3mb4HAxEb9iD3o0W .node .label text,#mermaid-svg-3mb4HAxEb9iD3o0W .image-shape .label,#mermaid-svg-3mb4HAxEb9iD3o0W .icon-shape .label{text-anchor:middle;}#mermaid-svg-3mb4HAxEb9iD3o0W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3mb4HAxEb9iD3o0W .rough-node .label,#mermaid-svg-3mb4HAxEb9iD3o0W .node .label,#mermaid-svg-3mb4HAxEb9iD3o0W .image-shape .label,#mermaid-svg-3mb4HAxEb9iD3o0W .icon-shape .label{text-align:center;}#mermaid-svg-3mb4HAxEb9iD3o0W .node.clickable{cursor:pointer;}#mermaid-svg-3mb4HAxEb9iD3o0W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3mb4HAxEb9iD3o0W .arrowheadPath{fill:#333333;}#mermaid-svg-3mb4HAxEb9iD3o0W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3mb4HAxEb9iD3o0W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3mb4HAxEb9iD3o0W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3mb4HAxEb9iD3o0W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3mb4HAxEb9iD3o0W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3mb4HAxEb9iD3o0W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster text{fill:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W .cluster span{color:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3mb4HAxEb9iD3o0W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3mb4HAxEb9iD3o0W rect.text{fill:none;stroke-width:0;}#mermaid-svg-3mb4HAxEb9iD3o0W .icon-shape,#mermaid-svg-3mb4HAxEb9iD3o0W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3mb4HAxEb9iD3o0W .icon-shape p,#mermaid-svg-3mb4HAxEb9iD3o0W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3mb4HAxEb9iD3o0W .icon-shape .label rect,#mermaid-svg-3mb4HAxEb9iD3o0W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3mb4HAxEb9iD3o0W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3mb4HAxEb9iD3o0W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3mb4HAxEb9iD3o0W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文本与数值
数据处理
基础
严格模式
let
const
块级作用域
数组解构
对象解构
模板字符串
字符串新方法
数值扩展
【代码注释】依赖图强调前置关系:不理解 TDZ 则解构默认值中的引用易错;不熟模板字符串则后续框架插值难对应源码。复习时可沿箭头自测每节点能否口述 + 写 5 行示例。
8.3 示例索引(完整可运行 HTML)
| 主题 | 章节锚点 |
|---|---|
| ES5 严格模式 | [2.0](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| let 关键字 | [2.4.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| TDZ 底层机制 | [2.1.2.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 块级作用域与列表点击 | [2.4.2](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| const 常量 | [2.4.3](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 数组解构 | [3.3.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 对象解构 | [3.3.2](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| for...of + 解构 | [3.2.3.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 模板字符串与列表渲染 | [4.3.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 字符串新方法 | [4.3.2](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| String.at() / String.raw | [4.2.4.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 浮点精度 Number.EPSILON | [5.2.2.1](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
可选链 ?. 与 ?? |
[5.7](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
逻辑赋值 ??= / ` |
|
| const + deepFreeze 不可变对象 | [2.2.5](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
| 标签模板:styled-components / GraphQL / SQL | [4.1.3 用法四](#主题 章节锚点 ES5 严格模式 2.0 let 关键字 2.4.1 TDZ 底层机制 2.1.2.1 块级作用域与列表点击 2.4.2 const 常量 2.4.3 数组解构 3.3.1 对象解构 3.3.2 for…of + 解构 3.2.3.1 模板字符串与列表渲染 4.3.1 字符串新方法 4.3.2 String.at() / String.raw 4.2.4.1 浮点精度 Number.EPSILON 5.2.2.1 可选链 ?. 与 ?? 5.7 逻辑赋值 ??= / |
8.4 选型速查:何时用哪一项
渲染错误: Mermaid 渲染失败: Parse error on line 15: ...级,'' 也算空| NCA2逻辑赋值 \|\|= N -->|仅值存在时才更 -----------------------^ Expecting 'SQE', 'TAGEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PIPE'
【代码注释】决策流:可变用 let、固定引用用 const、配置枚举用 deepFreeze;有序数据数组解构、配置对象用对象解构;多行/插值用模板字符串、DSL/XSS 防护用标签模板;深层属性访问用 ?.,设默认值用 ??,赋默认值用 ??=/||=/&&=;浮点比较用 Number.EPSILON。
结语
ES6+为JavaScript带来了革命性的变化,不仅提升了语言的表达能力,还大大改善了开发体验。通过掌握这些新特性,开发者可以:
- 编写更安全、更可靠的代码:块级作用域、常量、新的数据类型
- 提高开发效率:解构赋值、模板字符串、箭头函数
- 更好地处理异步操作:Promise、async/await
- 构建更复杂的应用:类、模块、新的数据结构
在实际开发中,建议:
- 优先使用const和let,避免var
- 合理使用解构赋值,简化代码
- 充分利用模板字符串,提高可读性
- 善用异步处理新特性,避免回调地狱
- 关注性能和兼容性,合理选择转译工具
随着JavaScript生态的不断发展,ES6+特性已成为现代前端开发的基础技能。持续学习和实践这些特性,将帮助开发者构建更加优雅、高效的应用程序。
参考资源:
持续学习资源:
- JavaScript Weekly
- ES6 Features
- Can I Use - 浏览器兼容性查询
希望这份ES6+专业指南能够帮助您更好地理解和应用现代JavaScript特性!