ES6完全指南:从入门到精通的现代化JavaScript开发
面向 Day04 的 ES6 总结博客:数据类型、变量声明、数组扁平化/拷贝、对象属性描述符与深拷贝。理论参考 MDN JavaScript、ECMA-262。
目录
- [0. 知识脉络与名词百科](#0. 知识脉络与名词百科)
- [1. ECMAScript概述](#1. ECMAScript概述)
- [2. 数据类型系统](#2. 数据类型系统)
- [3. 变量声明方式](#3. 变量声明方式)
- [3.5 箭头函数](#3.5 箭头函数)
- [3.6 模板字符串](#3.6 模板字符串)
- [3.7 解构赋值](#3.7 解构赋值)
- [3.8 展开运算符与 Rest 参数](#3.8 展开运算符与 Rest 参数)
- [3.9 ES6 模块系统](#3.9 ES6 模块系统)
- [3.10 ES6 Class 类系统](#3.10 ES6 Class 类系统)
- [3.11 Iterator 与 Generator](#3.11 Iterator 与 Generator)
- [3.12 Proxy 与 Reflect](#3.12 Proxy 与 Reflect)
- [3.13 可选链与空值合并](#3.13 可选链与空值合并)
- [4. 数组操作深度解析](#4. 数组操作深度解析)
- [5. 对象属性特性详解](#5. 对象属性特性详解)
- [6. 深拷贝与浅拷贝](#6. 深拷贝与浅拷贝)
- [7. 实战应用场景](#7. 实战应用场景)
- [8. 性能优化建议](#8. 性能优化建议)
- [9. 最佳实践总结](#9. 最佳实践总结)
- [10. Day04 知识点速查](#10. Day04 知识点速查)
- 附录:官方与延伸阅读
0. 知识脉络与名词百科
0.1 本章知识图谱
Day04 在 ES6 语法底座上,聚焦 类型体系 → 变量声明 → 数组工具 → 对象描述符 → 拷贝策略 五条主线。
#mermaid-svg-3umzz3W5HVnDWhpJ{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-3umzz3W5HVnDWhpJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3umzz3W5HVnDWhpJ .error-icon{fill:#552222;}#mermaid-svg-3umzz3W5HVnDWhpJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3umzz3W5HVnDWhpJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3umzz3W5HVnDWhpJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3umzz3W5HVnDWhpJ .marker.cross{stroke:#333333;}#mermaid-svg-3umzz3W5HVnDWhpJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3umzz3W5HVnDWhpJ p{margin:0;}#mermaid-svg-3umzz3W5HVnDWhpJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster-label text{fill:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster-label span{color:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster-label span p{background-color:transparent;}#mermaid-svg-3umzz3W5HVnDWhpJ .label text,#mermaid-svg-3umzz3W5HVnDWhpJ span{fill:#333;color:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ .node rect,#mermaid-svg-3umzz3W5HVnDWhpJ .node circle,#mermaid-svg-3umzz3W5HVnDWhpJ .node ellipse,#mermaid-svg-3umzz3W5HVnDWhpJ .node polygon,#mermaid-svg-3umzz3W5HVnDWhpJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3umzz3W5HVnDWhpJ .rough-node .label text,#mermaid-svg-3umzz3W5HVnDWhpJ .node .label text,#mermaid-svg-3umzz3W5HVnDWhpJ .image-shape .label,#mermaid-svg-3umzz3W5HVnDWhpJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-3umzz3W5HVnDWhpJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3umzz3W5HVnDWhpJ .rough-node .label,#mermaid-svg-3umzz3W5HVnDWhpJ .node .label,#mermaid-svg-3umzz3W5HVnDWhpJ .image-shape .label,#mermaid-svg-3umzz3W5HVnDWhpJ .icon-shape .label{text-align:center;}#mermaid-svg-3umzz3W5HVnDWhpJ .node.clickable{cursor:pointer;}#mermaid-svg-3umzz3W5HVnDWhpJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3umzz3W5HVnDWhpJ .arrowheadPath{fill:#333333;}#mermaid-svg-3umzz3W5HVnDWhpJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3umzz3W5HVnDWhpJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3umzz3W5HVnDWhpJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3umzz3W5HVnDWhpJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3umzz3W5HVnDWhpJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3umzz3W5HVnDWhpJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster text{fill:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ .cluster span{color:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ 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-3umzz3W5HVnDWhpJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3umzz3W5HVnDWhpJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-3umzz3W5HVnDWhpJ .icon-shape,#mermaid-svg-3umzz3W5HVnDWhpJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3umzz3W5HVnDWhpJ .icon-shape p,#mermaid-svg-3umzz3W5HVnDWhpJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3umzz3W5HVnDWhpJ .icon-shape .label rect,#mermaid-svg-3umzz3W5HVnDWhpJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3umzz3W5HVnDWhpJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3umzz3W5HVnDWhpJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3umzz3W5HVnDWhpJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据类型
let const 块级
箭头函数 模板字符串
解构 展开 Rest
ES6 模块 ESM
Class 类系统
Iterator Generator
Proxy Reflect
数组 flat 拷贝
属性描述符 访问器
深拷贝 浅拷贝
业务实战
【代码注释】 完整知识主线:类型体系(值/引用)→ 块级变量(let/const/TDZ)→ 函数增强(箭头函数/词法 this)→ 解构/展开/Rest → ES6 模块(ESM/Tree Shaking)→ Class 类系统(私有字段/继承/Mixin)→ Iterator/Generator(惰性求值/状态机)→ Proxy/Reflect(响应式/元编程)→ 数组工具(flat/拷贝)→ 描述符/访问器 → 拷贝策略(浅/深/structuredClone)。这些特性在框架源码中深度交织:Vue 3 = Proxy + Reflect;Redux = 展开运算符 + 不可变更新;React Hooks = 闭包 + Generator 思想。
0.2 核心名词解析
| 名词 | 解析 |
|---|---|
| 原始类型 | number、string、boolean、null、undefined、bigint、symbol;值传递 |
| 引用类型 | Object、Array、Function 等;变量存地址,赋值共享引用 |
| 块级作用域 | {} 内 let/const 仅块内可见,无 var 提升问题 |
| TDZ | 暂时性死区(Temporal Dead Zone),let/const 声明前不可访问 |
| 属性描述符 | value/writable/enumerable/configurable 控制属性行为 |
| 访问器属性 | get/set,用于计算属性、数据校验(Vue 响应式基础) |
| 浅拷贝 | 只复制第一层;[...arr]、Object.assign |
| 深拷贝 | 递归复制嵌套结构;需注意循环引用与特殊类型 |
| Iterator | 有 next() 的对象,每次返回 {value, done},驱动 for...of |
| Generator | function* 函数,yield 暂停执行,实现惰性求值与协程 |
| Proxy | 拦截对象操作(读/写/删除等),可插入校验、日志、响应式逻辑 |
可选链 ?. |
访问链中遇 null/undefined 短路返回 undefined,不抛错 |
空值合并 ?? |
仅 null/undefined 时回退,保留 0、''、false 等合法假值 |
业界典型落地:
- React:不可变更新依赖浅拷贝 + 展开运算符;Fiber 调度借鉴 Generator 协程思想
- Vue 2 :
Object.defineProperty基于属性描述符实现响应式 - Vue 3 :
Proxy+Reflect实现细粒度依赖收集,解决 Vue 2 数组/新增属性检测盲区 - Redux Toolkit :
structuredClone/Immer 实现不可变状态更新 - lodash :
cloneDeep与手写深拷贝的取舍 - styled-components / graphql-tag:标签模板字符串(Tagged Template)实现 DSL
1. ECMAScript概述
1.1 什么是ECMAScript?
ECMAScript是JavaScript的标准化规范,由ECMA国际组织(欧洲计算机制造商协会)制定和维护。它是JavaScript的核心语法和功能基础,而JavaScript则是ECMAScript规范的具体实现。
名词解析:
- ECMA: European Computer Manufacturers Association(欧洲计算机制造商协会)
- Script: 脚本语言
- ES6: ECMAScript 2015的别名,是JavaScript历史上最重要的更新之一
- TC39: 负责ECMAScript标准化的技术委员会
1.2 ES6的重要意义
ES6(ECMAScript 2015)是JavaScript发展史上的重要里程碑,引入了大量现代化的语法特性和功能,极大地提升了开发效率和代码质量。
核心特性概览:
#mermaid-svg-jkmm98xX9OV4f6I4{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-jkmm98xX9OV4f6I4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jkmm98xX9OV4f6I4 .error-icon{fill:#552222;}#mermaid-svg-jkmm98xX9OV4f6I4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jkmm98xX9OV4f6I4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jkmm98xX9OV4f6I4 .marker.cross{stroke:#333333;}#mermaid-svg-jkmm98xX9OV4f6I4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jkmm98xX9OV4f6I4 p{margin:0;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge{stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 text{fill:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth--1{stroke-width:17;}#mermaid-svg-jkmm98xX9OV4f6I4 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-0{stroke-width:14;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-1{stroke-width:11;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 text{fill:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-2{stroke-width:8;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-3{stroke-width:5;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-4{stroke-width:2;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-5{stroke-width:-1;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-6{stroke-width:-4;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-7{stroke-width:-7;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-8{stroke-width:-10;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-9{stroke-width:-13;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 polygon,#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 text{fill:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .edge-depth-10{stroke-width:-16;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled circle,#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:lightgray;}#mermaid-svg-jkmm98xX9OV4f6I4 .disabled text{fill:#efefef;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-root rect,#mermaid-svg-jkmm98xX9OV4f6I4 .section-root path,#mermaid-svg-jkmm98xX9OV4f6I4 .section-root circle,#mermaid-svg-jkmm98xX9OV4f6I4 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-jkmm98xX9OV4f6I4 .section-root text{fill:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-root span{color:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .section-2 span{color:#ffffff;}#mermaid-svg-jkmm98xX9OV4f6I4 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-jkmm98xX9OV4f6I4 .edge{fill:none;}#mermaid-svg-jkmm98xX9OV4f6I4 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-jkmm98xX9OV4f6I4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ES6核心特性
变量声明
let
const
块级作用域
函数增强
箭头函数
默认参数
rest参数
模板字符串
数据结构
解构赋值
扩展运算符
新增对象类型
面向对象
class语法
继承
模块化
【代码注释】 ES6 核心特性思维导图:let/const 块级作用域、箭头函数词法 this、解构赋值、Class/模块------每项特性都是下一节内容的前置知识。
2. 数据类型系统
2.1 完整的数据类型分类
JavaScript数据类型体系架构:
#mermaid-svg-lEmpas4h40kiTxg0{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-lEmpas4h40kiTxg0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lEmpas4h40kiTxg0 .error-icon{fill:#552222;}#mermaid-svg-lEmpas4h40kiTxg0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lEmpas4h40kiTxg0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lEmpas4h40kiTxg0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lEmpas4h40kiTxg0 .marker.cross{stroke:#333333;}#mermaid-svg-lEmpas4h40kiTxg0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lEmpas4h40kiTxg0 p{margin:0;}#mermaid-svg-lEmpas4h40kiTxg0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lEmpas4h40kiTxg0 .cluster-label text{fill:#333;}#mermaid-svg-lEmpas4h40kiTxg0 .cluster-label span{color:#333;}#mermaid-svg-lEmpas4h40kiTxg0 .cluster-label span p{background-color:transparent;}#mermaid-svg-lEmpas4h40kiTxg0 .label text,#mermaid-svg-lEmpas4h40kiTxg0 span{fill:#333;color:#333;}#mermaid-svg-lEmpas4h40kiTxg0 .node rect,#mermaid-svg-lEmpas4h40kiTxg0 .node circle,#mermaid-svg-lEmpas4h40kiTxg0 .node ellipse,#mermaid-svg-lEmpas4h40kiTxg0 .node polygon,#mermaid-svg-lEmpas4h40kiTxg0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lEmpas4h40kiTxg0 .rough-node .label text,#mermaid-svg-lEmpas4h40kiTxg0 .node .label text,#mermaid-svg-lEmpas4h40kiTxg0 .image-shape .label,#mermaid-svg-lEmpas4h40kiTxg0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-lEmpas4h40kiTxg0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lEmpas4h40kiTxg0 .rough-node .label,#mermaid-svg-lEmpas4h40kiTxg0 .node .label,#mermaid-svg-lEmpas4h40kiTxg0 .image-shape .label,#mermaid-svg-lEmpas4h40kiTxg0 .icon-shape .label{text-align:center;}#mermaid-svg-lEmpas4h40kiTxg0 .node.clickable{cursor:pointer;}#mermaid-svg-lEmpas4h40kiTxg0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lEmpas4h40kiTxg0 .arrowheadPath{fill:#333333;}#mermaid-svg-lEmpas4h40kiTxg0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lEmpas4h40kiTxg0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lEmpas4h40kiTxg0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lEmpas4h40kiTxg0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lEmpas4h40kiTxg0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lEmpas4h40kiTxg0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lEmpas4h40kiTxg0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lEmpas4h40kiTxg0 .cluster text{fill:#333;}#mermaid-svg-lEmpas4h40kiTxg0 .cluster span{color:#333;}#mermaid-svg-lEmpas4h40kiTxg0 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-lEmpas4h40kiTxg0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lEmpas4h40kiTxg0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-lEmpas4h40kiTxg0 .icon-shape,#mermaid-svg-lEmpas4h40kiTxg0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lEmpas4h40kiTxg0 .icon-shape p,#mermaid-svg-lEmpas4h40kiTxg0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lEmpas4h40kiTxg0 .icon-shape .label rect,#mermaid-svg-lEmpas4h40kiTxg0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lEmpas4h40kiTxg0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lEmpas4h40kiTxg0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lEmpas4h40kiTxg0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JavaScript数据类型
原始类型
对象类型
number数值
string字符串
boolean布尔
null空值
undefined未定义
bigint大整数
symbol符号
Array数组
Function函数
Object对象
Date日期
Set集合
WeakSet弱集合
Map映射
WeakMap弱映射
【代码注释】 JavaScript 数据类型树:7 种原始类型(值传递)+ 多种引用类型(引用传递)。掌握此图可有效避免「修改对象属性影响到原始变量」类 Bug。
2.2 原始类型详解
名词解析:原始类型(Primitive Types)
原始类型是JavaScript中最基本的数据类型,它们是不可变的,直接存储在栈内存中。
| 类型 | 说明 | 示例 | 典型应用场景 |
|---|---|---|---|
number |
数值类型,包括整数和浮点数 | 42, 3.14, NaN |
数学计算、统计数据 |
string |
字符串类型 | "hello", 'world' |
文本处理、用户输入 |
boolean |
布尔类型 | true, false |
条件判断、状态标记 |
null |
空值,表示"无"的对象 | null |
重置变量、清空引用 |
undefined |
未定义值 | undefined |
变量未初始化 |
bigint |
大整数类型 | 9007199254740991n |
精确的大数计算 |
symbol |
唯一标识符 | Symbol('id') |
对象属性键、避免冲突 |
实战示例:
javascript
// 【代码注释】原始类型实战:BigInt 与 Symbol 的典型用法与易错点
// Number 在 ±2^53-1 之外会丢失精度(浮点二进制表示的局限)
const maxSafeNumber = Number.MAX_SAFE_INTEGER; // 9007199254740991,安全整数上界
const unsafe = 9007199254740992; // 与 9007199254740993 在 Number 下可能相等
const bigNumber = 9007199254740992n; // 字面量后缀 n 表示 BigInt,任意大整数精确运算
// Symbol:每次 Symbol() 都生成全局唯一值,description 仅用于调试显示,不参与相等性比较
const id1 = Symbol('id');
const id2 = Symbol('id');
console.log(id1 === id2); // false ------ 不能用 === 判断「同名」Symbol
// 计算属性名 [Symbol('id')]:该键不会出现在 Object.keys / for...in 中(除非 enumerable: true)
const user = {
[Symbol('id')]: 123,
name: "张三",
age: 25
};
【代码注释】 Number.MAX_SAFE_INTEGER 标识安全整数边界,超出后应改用 BigInt(n 后缀);Symbol 适合作为「私有」或库内部属性键,避免与业务字段名冲突,且默认不可枚举,常用于框架内部元数据(如 React 元素类型标记)。
2.3 对象类型详解
名词解析:对象类型(Object Types)
对象类型是复合数据类型,可以存储多个值和复杂的数据结构。对象存储在堆内存中,变量通过引用访问对象。
核心对象类型应用场景:
javascript
// 【代码注释】Array:有序、可重复,下标访问 O(1),适合列表、队列、表格行数据
const shoppingCart = ['苹果', '香蕉', '橙子'];
// 注意:数组是引用类型,赋值/传参只复制引用,修改元素会影响同一引用
// 【代码注释】Set:基于 SameValueZero 去重,插入/查找平均 O(1),值唯一
const uniqueTags = new Set(['JavaScript', 'React', 'JavaScript']);
console.log(uniqueTags.size); // 2 ------ 重复 'JavaScript' 被自动忽略
// 与 Array.filter 去重相比:语义更清晰,且 has/add/delete 语义专为集合设计
// 【代码注释】Map:键可为任意类型(对象、函数),保持插入顺序,适合非字符串键的 KV 存储
const userSessions = new Map();
userSessions.set('session_123', { userId: 1, loginTime: Date.now() });
// 对比普通对象:Map 键数量用 .size 获取,无原型链污染,频繁增删性能更稳定
// 【代码注释】Date:解析 ISO 8601 字符串;时区与本地化显示需配合 toLocaleString 等 API
const appointment = new Date('2024-12-25T10:00:00');
// 注意:月份在部分 API 中为 0-11(getMonth),与日常「1 月=1」不一致
【代码注释】 选型口诀:要顺序与重复 → Array;要唯一集合 → Set;键非字符串或需稳定迭代顺序 → Map;时间轴与定时 → Date。四者均为引用类型,变量存的是堆上对象的地址。
实际应用示例:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript数据类型实际应用</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.demo-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.demo-section h3 {
color: #667eea;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.button {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
transition: background 0.3s;
}
.button:hover {
background: #764ba2;
}
.result {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-top: 10px;
font-family: 'Courier New', monospace;
}
.tag {
display: inline-block;
background: #e3f2fd;
color: #1976d2;
padding: 5px 10px;
border-radius: 15px;
margin: 5px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>JavaScript数据类型实战应用</h1>
<div class="demo-section">
<h3>🛒 购物车系统(Array应用)</h3>
<button class="button" onclick="addToCart()">添加商品</button>
<button class="button" onclick="removeFromCart()">移除商品</button>
<button class="button" onclick="showCart()">查看购物车</button>
<div id="cartResult" class="result"></div>
</div>
<div class="demo-section">
<h3>🏷️ 标签管理系统(Set应用)</h3>
<input type="text" id="tagInput" placeholder="输入标签">
<button class="button" onclick="addTag()">添加标签</button>
<div id="tagsResult" class="result"></div>
</div>
<div class="demo-section">
<h3>⏰ 会话管理系统(Map应用)</h3>
<button class="button" onclick="createSession()">创建会话</button>
<button class="button" onclick="checkSession()">检查会话</button>
<div id="sessionResult" class="result"></div>
</div>
</div>
<script>
// 【代码注释】模块一:购物车 ------ 演示 Array 的 push/pop/map 与 DOM 联动
let shoppingCart = []; // let:购物车内容会随用户操作变化,需重新赋值/变异
const products = ['iPhone 15', 'MacBook Pro', 'AirPods', 'iPad', 'Apple Watch'];
function addToCart() {
// Math.floor(Math.random() * length) 生成 [0, length-1] 均匀随机下标
const randomProduct = products[Math.floor(Math.random() * products.length)];
shoppingCart.push(randomProduct); // 尾部入队,均摊 O(1)
showCart(); // 每次变更后同步刷新视图(简单单向数据流)
}
function removeFromCart() {
if (shoppingCart.length > 0) {
shoppingCart.pop(); // 移除最后一项;空数组 pop 返回 undefined 但不抛错
showCart();
}
}
function showCart() {
const resultDiv = document.getElementById('cartResult');
if (shoppingCart.length === 0) {
resultDiv.innerHTML = '购物车为空';
} else {
// map 生成带序号的 HTML 片段,join 拼接;注意此处未转义,生产环境需防 XSS
resultDiv.innerHTML = `
<strong>购物车内容:</strong><br>
${shoppingCart.map((item, index) => `${index + 1}. ${item}`).join('<br>')}
<br><strong>商品数量:</strong> ${shoppingCart.length}
`;
}
}
// 【代码注释】模块二:标签 ------ Set 保证用户输入的标签名全局唯一
const tags = new Set(['JavaScript', '前端开发', 'Web技术']);
function addTag() {
const input = document.getElementById('tagInput');
const newTag = input.value.trim(); // trim 过滤纯空格,避免无效条目
if (newTag) {
tags.add(newTag); // 已存在则静默忽略,Set 不抛错也不重复存储
input.value = '';
showTags();
}
}
function showTags() {
const resultDiv = document.getElementById('tagsResult');
// Set 不可直接 map,先 Array.from 转为数组再渲染(保持插入顺序)
resultDiv.innerHTML = `
<strong>当前标签:</strong><br>
${Array.from(tags).map(tag => `<span class="tag">${tag}</span>`).join('')}
<br><strong>标签数量:</strong> ${tags.size}(自动去重)
`;
}
showTags(); // 页面加载时渲染初始标签集合
// 【代码注释】模块三:会话 ------ Map 以 sessionId 为键,值为会话元数据对象
const sessions = new Map();
function createSession() {
const sessionId = 'session_' + Date.now(); // 时间戳保证键在单机演示中唯一
const sessionData = {
userId: Math.floor(Math.random() * 1000),
loginTime: new Date().toLocaleString(), // 本地化时间字符串,便于直接展示
ip: '192.168.1.' + Math.floor(Math.random() * 255)
};
sessions.set(sessionId, sessionData); // O(1) 写入;值对象与键独立存储
showSessions();
}
function checkSession() {
const resultDiv = document.getElementById('sessionResult');
if (sessions.size === 0) {
resultDiv.innerHTML = '没有活跃会话';
} else {
// entries() 返回迭代器;.next().value 取第一条 [key, value],演示 Map 遍历协议
const firstSession = sessions.entries().next().value;
if (firstSession) {
const [sessionId, sessionData] = firstSession; // 数组解构,与 Map 条目结构一致
resultDiv.innerHTML = `
<strong>会话详情:</strong><br>
会话ID: ${sessionId}<br>
用户ID: ${sessionData.userId}<br>
登录时间: ${sessionData.loginTime}<br>
IP地址: ${sessionData.ip}<br>
<strong>活跃会话总数:</strong> ${sessions.size}
`;
}
}
}
function showSessions() {
checkSession(); // 复用检查逻辑,创建会话后立即展示
}
</script>
</body>
</html>
【代码注释】 完整可运行 HTML 示例:购物车(Array)、标签去重(Set)、会话管理(Map)三大场景并列演示;Set.add 自动去重,Map.set/get 支持任意类型键。
3. 变量声明方式
3.1 变量声明演进史
JavaScript变量声明的完整演进:
#mermaid-svg-x3Dmb7FKihYVrf56{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-x3Dmb7FKihYVrf56 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x3Dmb7FKihYVrf56 .error-icon{fill:#552222;}#mermaid-svg-x3Dmb7FKihYVrf56 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x3Dmb7FKihYVrf56 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x3Dmb7FKihYVrf56 .marker.cross{stroke:#333333;}#mermaid-svg-x3Dmb7FKihYVrf56 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x3Dmb7FKihYVrf56 p{margin:0;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge{stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 text{fill:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth--1{stroke-width:17;}#mermaid-svg-x3Dmb7FKihYVrf56 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-0{stroke-width:14;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-1{stroke-width:11;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 text{fill:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-2{stroke-width:8;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-3{stroke-width:5;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-4{stroke-width:2;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-5{stroke-width:-1;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-6{stroke-width:-4;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-7{stroke-width:-7;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-8{stroke-width:-10;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-9{stroke-width:-13;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 circle,#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 text{fill:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .edge-depth-10{stroke-width:-16;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-x3Dmb7FKihYVrf56 .lineWrapper line{stroke:black;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled circle,#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:lightgray;}#mermaid-svg-x3Dmb7FKihYVrf56 .disabled text{fill:#efefef;}#mermaid-svg-x3Dmb7FKihYVrf56 .section-root rect,#mermaid-svg-x3Dmb7FKihYVrf56 .section-root path,#mermaid-svg-x3Dmb7FKihYVrf56 .section-root circle{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-x3Dmb7FKihYVrf56 .section-root text{fill:#ffffff;}#mermaid-svg-x3Dmb7FKihYVrf56 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-x3Dmb7FKihYVrf56 .edge{fill:none;}#mermaid-svg-x3Dmb7FKihYVrf56 .eventWrapper{filter:brightness(120%);}#mermaid-svg-x3Dmb7FKihYVrf56 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1995 var 出现在JavaScript早期版本 2015 let/const 在ES6中引入 解决var的作用域问题 2015 class 引入类声明方式 2015 import 引入模块导入语法 JavaScript变量声明方式演进
【代码注释】 变量声明演进时间线:var(ES5,函数作用域,可提升)→ let/const(ES6,块级作用域,TDZ)→ class/import(ES6,静态分析友好)。
3.2 六种变量声明方式详解
完整的变量声明方式:
| 声明方式 | 引入版本 | 作用域 | 提升行为 | 重复声明 | 典型应用场景 |
|---|---|---|---|---|---|
var |
ES5 | 函数作用域 | 存在提升 | 允许 | 遗留代码维护 |
let |
ES6 | 块级作用域 | 暂时性死区 | 禁止 | 一般变量声明 |
const |
ES6 | 块级作用域 | 暂时性死区 | 禁止 | 常量声明 |
function |
ES5 | 函数作用域 | 完整提升 | 允许 | 函数声明 |
class |
ES6 | 块级作用域 | 暂时性死区 | 禁止 | 类定义 |
import |
ES6 | 模块作用域 | 静态提升 | 禁止 | 模块导入 |
3.3 let和const的核心特性
名词解析:暂时性死区(Temporal Dead Zone,TDZ)
暂时性死区是指从代码块开始到变量声明语句之间的区域,在这个区域内变量无法访问。
javascript
// 【代码注释】let / const 与 var 的行为差异 ------ 建议在控制台逐段取消注释观察报错
console.log('=== let和const核心特性演示 ===');
// ① 同一作用域内不可重复声明(编译期 SyntaxError,优于运行时才发现的 var 覆盖)
let userName = "张三";
// let userName = "李四"; // SyntaxError: Identifier 'userName' has already been declared
const MAX_USERS = 100;
// const MAX_USERS = 200; // 同上;const 还要求声明时必须初始化
// ② 暂时性死区 TDZ:从块开始到 let/const 语句之前,访问标识符会抛 ReferenceError
// console.log(age); // ReferenceError: Cannot access 'age' before initialization
let age = 25; // 与 var 不同:var 会提升并初始化为 undefined
// ③ 块级作用域:仅 {} / if / for 等块内有效,块外无法访问(避免 var 泄漏到外层)
{
let blockScoped = "只在块内可见";
const BLOCK_CONSTANT = "块级常量"; // const 绑定不可重新赋值,但对象属性仍可改
console.log('块内访问:', blockScoped);
}
// console.log(blockScoped); // ReferenceError: blockScoped is not defined
// ④ 脚本顶层 let/const 不成为 window 属性,减少全局命名空间污染(模块化前提)
let globalLet = "全局let";
const globalConst = "全局const";
var globalVar = "全局var"; // var 在浏览器全局脚本中会挂到 window
console.log('window.globalVar:', window.globalVar); // "全局var"
console.log('window.globalLet:', window.globalLet); // undefined
console.log('window.globalConst:', window.globalConst); // undefined
【代码注释】 四条规则对应四条防线:防重复声明、防「先用后声明」、防作用域泄漏、防污染 window。面试常问:TDZ 在 typeof 未声明变量时仍抛错;const 冻结的是绑定而非对象内部属性。
3.4 变量声明最佳实践
实战应用场景:
javascript
// 【代码注释】声明策略:默认 const,只有需要重新绑定标识符时才用 let,避免 var
// 模块级常量:引用地址不变,业务上视为「配置项」
const API_BASE_URL = 'https://api.example.com';
const DEFAULT_TIMEOUT = 5000;
const CONFIG = {
debug: true,
version: '1.0.0'
}; // 注意:CONFIG 本身不能 CONFIG = {},但 CONFIG.debug 仍可改,除非再 freeze
// 会随用户交互或请求结果变化的状态,用 let
let currentPage = 1;
let searchQuery = '';
let userSession = null; // 先 null 占位,登录后再赋对象引用
// 【代码注释】类 + 静态字段 + 浅冻结:适合应用启动时一次性写入的配置
class AppConfig {
// static 字段属于类本身,所有实例共享,常用于版本号、重试上限等
static API_VERSION = 'v2';
static MAX_RETRIES = 3;
static DEFAULT_LOCALE = 'zh-CN';
constructor(config = {}) {
// 外层 freeze:禁止替换 this.config 或增删顶层键
// 内层 endpoints 再 freeze:防止运行时被改掉路径常量
this.config = Object.freeze({
timeout: config.timeout || 30000,
debug: config.debug || false,
endpoints: Object.freeze({
users: '/users',
posts: '/posts',
comments: '/comments'
})
});
}
}
const appConfig = new AppConfig({ timeout: 5000 });
// appConfig.config.timeout = 10000;
// 非严格模式:静默失败;严格模式:TypeError(属性不可写)
// 深层嵌套对象若未 freeze,仍可能被修改 ------ 深冻结需递归或 structuredClone + 只读代理
【代码注释】 ESLint prefer-const 背后的理由:减少可变状态,便于推理与 Tree Shaking。Object.freeze 是浅冻结,嵌套对象需逐层处理;与 readonly(TS)或 Immutable 数据结构配合才是生产级不可变配置。
3.5 箭头函数(Arrow Function)
3.5.1 语法与 this 绑定原理
为什么需要箭头函数?
传统函数的 this 由调用时 动态决定,这在回调中极易引发 this 丢失问题。箭头函数没有自己的 this------它从定义时的词法作用域 捕获外层 this,彻底解决了这类痛点。
javascript
// 【代码注释】对比:普通函数作为回调时 this 由「谁调用」决定,箭头函数由「定义处外层」决定
// ❌ setInterval 以普通函数调用回调,严格模式下 this 为 undefined,非严格为 window
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++; // 实际修改的是 window.seconds 或报错,与 Timer 实例无关
console.log(this.seconds); // NaN 或 undefined
}, 1000);
}
// ✅ 箭头函数不绑定自己的 this,闭包捕获构造执行时的 this(即 new Timer() 的实例)
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 词法 this 指向 Timer 实例
console.log(this.seconds); // 1, 2, 3 ...
}, 1000);
}
【代码注释】 箭头函数的核心价值:词法 this 捕获------定义时绑定外层 this,避免回调/定时器中 this 指向 window 或 undefined 的经典陷阱。
语法简化规则:
javascript
// 【代码注释】箭头函数语法糖:参数列表 → => → 表达式或块;无 function 关键字
// 单参数可省略圆括号(0 个或多于 1 个参数必须写括号)
const double = n => n * 2; // 单表达式自动作为返回值,等价于 return n * 2
const add = (a, b) => a + b; // 多参数必须 (a, b)
// 多条语句必须用 {} 包裹,且需显式 return(否则返回 undefined)
const clamp = (val, min, max) => {
if (val < min) return min;
if (val > max) return max;
return val;
};
// 返回对象字面量时,外层加 (),否则 { 会被解析为函数体块而非对象
const toPoint = (x, y) => ({ x, y }); // 等价于 return { x: x, y: y }
【代码注释】 箭头函数语法糖规则:单参数可省括号、单表达式可省 return、直接返回对象字面量须用 () 包裹(否则 {} 被解析为函数体)。
3.5.2 箭头函数的限制
| 特性 | 传统函数 | 箭头函数 |
|---|---|---|
this 绑定 |
动态(调用时决定) | 词法(定义时决定) |
arguments 对象 |
✅ 有 | ❌ 无(用 ...args 替代) |
| 作为构造函数 | ✅ 可以 new |
❌ 不可以 |
prototype 属性 |
✅ 有 | ❌ 无 |
generator 函数 |
✅ 可以 | ❌ 不可以 |
javascript
// 【代码注释】何时不用箭头函数:需要动态 this、构造函数、generator、依赖 arguments 时
const counter = {
count: 0,
// ❌ 对象字面量中的箭头方法:this 指向定义时外层(通常是全局),不是 counter
increment: () => { counter.count++; }, // 只能硬编码 counter,无法通用复用
// ✅ ES6 方法简写:调用时 this 绑定为 counter
decrement() { this.count--; }
};
// DOM 事件:浏览器以「元素」为 this 调用监听器;箭头函数会丢失 element 引用
button.addEventListener('click', function () {
this.classList.toggle('active'); // this === 被点击的 button
});
// 若必须用箭头函数,应改用 event.currentTarget 代替 this
【代码注释】 箭头函数四大限制:无自身 this、无 arguments、不能 new、无 prototype。对象方法和 DOM 事件处理仍需传统函数,避免 this 错误。
3.5.3 经典应用场景
javascript
// 【代码注释】场景一:声明式数组管道 ------ 每步返回新数组/值,配合箭头函数可读性高
const orders = [
{ id: 1, amount: 200, status: 'paid' },
{ id: 2, amount: 500, status: 'pending' },
{ id: 3, amount: 150, status: 'paid' },
];
const paidTotal = orders
.filter(o => o.status === 'paid') // 谓词筛选,保留 paid 订单
.map(o => o.amount) // 投影为 number 数组
.reduce((sum, amt) => sum + amt, 0); // 累加器初始值 0,避免空数组得到 undefined
console.log('已支付总额:', paidTotal); // 350
// 【代码注释】场景二:Promise 链中若写 function(){ this.token=... },this 会丢失
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL;
this.token = null;
}
login(credentials) {
return fetch(`${this.baseURL}/login`, {
method: 'POST',
body: JSON.stringify(credentials)
})
.then(res => res.json())
.then(data => {
this.token = data.token; // 箭头函数捕获类方法调用时的 this
return data.user;
});
}
}
// 【代码注释】场景三:柯里化 ------ 先固定 factor,再得到一元函数,便于复用与组合
const multiply = factor => value => value * factor;
const double = multiply(2); // 闭包保存 factor=2
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
【代码注释】 三大经典应用:链式数组操作(filter/map/reduce)代码极简;Promise 链避免 this 丢失;柯里化函数工厂(multiply(2) 返回 double)。
3.6 模板字符串(Template Literals)
3.6.1 核心语法与特性
模板字符串使用反引号(`````)定义,支持多行文本和内嵌表达式,彻底取代了繁琐的字符串拼接。
javascript
// 【代码注释】模板字符串(Template Literals):反引号 ` 包裹,${} 内为运行时求值的表达式
const user = { name: '张三', age: 28, vip: true };
// ❌ + 拼接:类型隐式转换、括号层级难维护、换行需 \n
const greetingOld = '你好,' + user.name + '!' + (user.vip ? '(VIP)' : '') + '\n年龄:' + user.age;
// ✅ 模板字符串:插值与换行原生支持,逻辑与展示结构一致
const greeting = `你好,${user.name}!${user.vip ? '(VIP)' : ''}
年龄:${user.age}`;
const price = 299;
const tax = 0.08;
// ${} 内可写任意表达式,包括方法调用;结果会 toString 后嵌入
console.log(`含税价格:${(price * (1 + tax)).toFixed(2)} 元`);
// 反引号字符串保留源码中的换行与行首空格;.trim() 去掉首尾空白行,常用于 HTML/SQL 片段
const html = `
<div class="card">
<h2>${user.name}</h2>
<p>年龄:${user.age}</p>
</div>
`.trim();
【代码注释】 模板字符串支持多行与表达式插值,可读性远超字符串拼接。${expr} 中可写任意表达式;.trim() 消除模板首尾空白行,常用于生成 HTML 片段。
3.6.2 标签模板(Tagged Templates)
标签模板是模板字符串的高级用法,允许用函数解析模板------这是许多库(如 styled-components、graphql、sql 模板)的底层机制。
javascript
// 【代码注释】Tagged Template:tag`...${a}...${b}` 等价于 tag(['...', '...'], a, b)
// strings:由静态文本分割得到的字符串数组;values:各 ${} 求值后的结果(rest 收集)
function highlight(strings, ...values) {
// strings.length === values.length + 1(首尾常有纯静态片段)
return strings.reduce((result, str, i) => {
const value = values[i] !== undefined
? `<mark>${values[i]}</mark>` // 对每个插值包裹高亮标签
: '';
return result + str + value;
}, '');
}
const name = '张三';
const score = 98;
const message = highlight`学员 ${name} 本次考试得了 ${score} 分,优秀!`;
// 【代码注释】标签模板可做「编译期/运行期」字符串处理:转义、国际化、样式组件等
function safeSQL(strings, ...values) {
const escaped = values.map(v =>
typeof v === 'string' ? v.replace(/'/g, "''") : v // SQL 单引号转义示意
);
return strings.reduce((q, s, i) => q + s + (escaped[i] ?? ''), '');
}
const userId = "1 OR 1=1";
const query = safeSQL`SELECT * FROM users WHERE id = '${userId}'`;
// 生产环境应使用参数化查询(Prepared Statement),此处仅演示标签函数机制
【代码注释】 标签模板(Tagged Template):函数名紧接反引号,第一参数为字符串片段数组,其余为插值。styled-components、gql、sql 等库均基于此机制。
3.7 解构赋值(Destructuring Assignment)
3.7.1 数组解构
原理:按位置从可迭代对象中提取值并绑定到变量。
javascript
// 【代码注释】数组解构:按索引位置一一绑定,右侧须为可迭代对象(数组、可迭代类数组)
const [first, second, third] = [10, 20, 30]; // 长度不足时,多余变量为 undefined
// 留空位跳过中间元素,只取第三个
const [, , z] = [1, 2, 3];
// 默认值仅在对应位置严格等于 undefined 时生效(null 不会触发默认值)
const [a = 0, b = 0] = [5];
console.log(a, b); // 5 0
// 解构赋值可交换变量,右侧先求值再赋值,无需临时变量
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
// rest 必须写在解构模式最后一位,得到真数组
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
function minMax(arr) {
return [Math.min(...arr), Math.max(...arr)]; // 用元组风格返回多值
}
const [min, max] = minMax([3, 1, 4, 1, 5, 9]);
console.log(`最小值 ${min},最大值 ${max}`);
【代码注释】 数组解构按索引位置匹配:可跳过元素(,)、设默认值、配合 ...rest 收集剩余项、交换变量(无需临时变量),函数返回多值时解构最为优雅。
3.7.2 对象解构
javascript
// 【代码注释】对象解构:按属性名匹配,与属性顺序无关
const user = {
id: 42,
name: '李四',
address: { city: '上海', district: '浦东' },
roles: ['admin', 'editor']
};
const { id, name } = user; // 变量名须与属性名一致(或用重命名语法)
// { 原属性名: 新变量名 } ------ 从 user.name 读到 userName
const { name: userName, id: userId } = user;
const { email = 'unknown@example.com' } = user; // user 无 email 时用默认
// 嵌套:先匹配 address,再从中解构 city、district
const { address: { city, district } } = user;
// rest 收集除 uid 外其余可枚举自有属性(浅拷贝一份新对象)
const { id: uid, ...rest } = user;
// 参数解构 + 嵌套默认值:address 缺失时用 {} 避免解构 undefined 报错
function renderUser({ name, email = '无', address: { city } = {} }) {
return `${name}(${email})- ${city}`;
}
renderUser(user);
【代码注释】 对象解构按属性名匹配:冒号后是新变量名(重命名),可设默认值,支持嵌套解构 { address: { city } },函数参数解构是最常见的实战场景。
3.7.3 经典应用场景
javascript
// 【代码注释】场景一:深层 API 响应一次解构到位,避免 response.data.data.user 链式取值
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const { data: { user, permissions }, meta: { total } } = await response.json();
return { user, permissions, total };
}
// 【代码注释】场景二:组件入参解构 = 自文档化 props API + 默认值(React/Vue 均常用)
function UserCard({ name, avatar, bio = '这个人很懒,什么都没留下', onClick }) {
return `<div onclick="${onClick}">${name}: ${bio}</div>`;
}
// 【代码注释】场景三:Map 迭代时 [key, value] 解构比 entry[0]/entry[1] 更清晰
const scores = new Map([['张三', 90], ['李四', 85], ['王五', 92]]);
for (const [name, score] of scores) {
console.log(`${name}:${score} 分`);
}
// 【代码注释】场景四:从模块按需解构子 API,利于 Tree Shaking 与避免 import * 整包
const { readFile, writeFile } = require('fs').promises;
import { ref, reactive, computed } from 'vue';
【代码注释】 解构的实战价值:减少临时变量、在函数签名处声明「需要哪些字段」、与 Map/for...of/import 组合时代码意图更直观;嵌套过深时建议配合可选链 ?. 或分步解构以提升可读性。
3.8 展开运算符与 Rest 参数
3.8.1 展开运算符(Spread ...)
展开运算符将可迭代对象"展开"为离散的元素,用于数组/对象的创建、合并、传参。
javascript
// 【代码注释】展开运算符 ...:在数组/对象/函数调用中「展开」可迭代对象或自有属性
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // 合并为新数组,不修改原数组
const copy = [...arr1]; // 浅拷贝:第一层元素引用仍与原数组共享
const divs = document.querySelectorAll('div');
const divArray = [...divs]; // 任意可迭代对象 → 真数组,从而可用 map/filter/reduce
const defaults = { theme: 'light', lang: 'zh-CN', fontSize: 14 };
const userPrefs = { theme: 'dark', fontSize: 16 };
const config = { ...defaults, ...userPrefs };
// 同名键后者覆盖前者;仅拷贝可枚举自有属性,不含原型链
// 【代码注释】不可变更新:每次返回新引用,便于 React diff / 时间旅行调试
const state = { user: { name: '张三', age: 25 }, count: 0 };
const newState = {
...state,
count: state.count + 1,
user: { ...state.user, age: 26 } // 嵌套层也需展开,否则仍共享 state.user 引用
};
const numbers = [3, 1, 4, 1, 5, 9];
console.log(Math.max(...numbers)); // 展开为离散参数,等价 Math.max(3, 1, 4, ...)
【代码注释】 展开运算符三大用途:数组/对象浅合并({ ...defaults, ...userPrefs })、不可变更新(Redux 核心模式)、类数组转真数组([...nodeList])。
3.8.2 Rest 参数(...args)
Rest 参数收集函数调用时剩余的实参,必须是参数列表中的最后一个,得到的是真正的数组。
javascript
// 【代码注释】Rest 参数:形式为 ...name,必须是形参列表最后一项,得到 Array 实例
function sum(first, ...rest) {
// first 绑定第一个实参,其余进入 rest 数组
return rest.reduce((acc, n) => acc + n, first);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
function logAll(label, ...items) {
// 对比 arguments:非数组、含命名参数之外信息;箭头函数无 arguments
items.forEach((item, i) => console.log(`${label}[${i}]:`, item));
}
// 【代码注释】装饰器模式:包装 fn 时保留其任意 arity
function withLogging(fn) {
return function (...args) {
console.log('调用参数:', args);
const result = fn(...args); // 再次展开,还原为 fn(a, b, ...) 调用形式
console.log('返回值:', result);
return result;
};
}
const loggedAdd = withLogging((a, b) => a + b);
loggedAdd(3, 4);
【代码注释】 Rest 参数收集剩余实参,得到真正的 Array(可用 map/filter),优于 arguments 伪数组。高阶函数透传参数时 ...args + fn(...args) 是标准写法。
3.9 ES6 模块系统(Modules)
3.9.1 模块化的意义
在模块化出现之前,JavaScript 所有文件共享全局作用域,大型项目极易发生命名冲突,依赖顺序也难以管理。ES6 模块(ESM)提供了文件级别的作用域 和静态的导入导出分析,使工具链可以做 Tree Shaking(摇树优化)。
3.9.2 导出(export)
javascript
// math.js ------ ES Module 在文件顶层静态分析 import/export,利于 Tree Shaking
// 【代码注释】具名导出:导入方必须使用相同导出名(或用 as 重命名),可导出任意多个
export const PI = 3.14159265;
export function add(a, b) { return a + b; }
export class Vector {
constructor(x, y) { this.x = x; this.y = y; }
magnitude() { return Math.sqrt(this.x ** 2 + this.y ** 2); }
}
// 先声明再导出,适合隐藏未 export 的模块内部实现
const _secret = 'internal'; // 未 export,其他文件无法 import
function helper() { return 42; }
export { helper as utilHelper }; // 对外名称 utilHelper
// 【代码注释】默认导出:一个模块最多一个 default,导入时可随意命名
export default class Calculator {
add(a, b) { return a + b; }
sub(a, b) { return a - b; }
}
【代码注释】 ES6 模块导出:具名导出(花括号)可多个;默认导出每模块唯一;导出时可重命名(as);Object.freeze 等声明后统一导出保持文件整洁。
3.9.3 导入(import)
javascript
// app.js ------ 浏览器须 <script type="module">;Node 需 "type":"module" 或 .mjs
// 具名导入:花括号内名称须与导出一致(编译期静态绑定)
import { PI, add, Vector } from './math.js';
import { utilHelper as helper } from './math.js'; // 本地别名,避免与当前文件变量冲突
import Calculator from './math.js'; // default 导入名可自定,常见写法与类名一致
import Calculator, { PI, add } from './math.js'; // 混合导入:default 写最前
import * as MathLib from './math.js'; // 命名空间对象:仅包含 export 的成员
console.log(MathLib.PI);
console.log(MathLib.add(1, 2));
// 【代码注释】动态 import():返回 Promise,可在运行时按条件加载(路由懒加载、CDN 分包)
async function loadModule() {
const { add } = await import('./math.js');
console.log(add(3, 4));
}
【代码注释】 模块导入:具名导入须花括号,名称必须匹配;默认导入名称自定义;* as Lib 命名空间导入;import() 动态导入返回 Promise,适合路由懒加载。
3.9.4 ESM vs CommonJS 对比
| 特性 | ES Modules(ESM) | CommonJS(CJS) |
|---|---|---|
| 语法 | import / export |
require / module.exports |
| 分析时机 | 静态(编译时) | 动态(运行时) |
| Tree Shaking | ✅ 支持 | ❌ 不支持 |
顶层 await |
✅ 支持 | ❌ 不支持 |
| 适用环境 | 浏览器原生 + Node.js(.mjs) | Node.js(传统) |
| 循环依赖 | 支持(导出 live binding) | 支持(但可能得到不完整对象) |
javascript
// 【代码注释】ESM Live Binding:导入的 let/const 绑定到导出模块的同一绑定,非快照拷贝
// counter.js
export let count = 0;
export function increment() { count++; }
// main.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1 ------ 无需重新 import,绑定自动反映最新值
// CommonJS:require 得到的是导出瞬间的值拷贝,后续修改不同步
【代码注释】 ESM Live Binding(活绑定):import { count } 拿到的是原模块变量的实时引用 ,increment() 后 count 自动更新。CJS require 是值拷贝,二者行为截然不同。
3.10 ES6 Class 类系统
3.10.1 Class 核心语法与原型关系
ES6 的 class 语法是原型继承的语法糖 ,底层仍然基于原型链(prototype),但语义更清晰、更接近传统面向对象。
javascript
// 【代码注释】class 是构造函数的语法糖,typeof Person === 'function'
// constructor 即原来的构造函数,方法写在 class 体内会自动挂到 prototype 上
class Person {
// 公有实例字段(ES2022,class fields 提案)
// 声明在 class 顶层,等价于在 constructor 中 this.name = name
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法 ------ 挂到 Person.prototype,所有实例共享一份,节省内存
greet() {
return `你好,我是 ${this.name},今年 ${this.age} 岁。`;
}
// 静态方法 ------ 挂到 Person 本身,实例无法访问,适合工厂/工具函数
static create(name, age) {
return new Person(name, age);
}
// getter/setter ------ 访问器属性,实例用法:p.info、p.age = 30
get info() {
return `${this.name}(${this.age})`;
}
set age(val) {
if (val < 0 || val > 150) throw new RangeError('年龄不合法');
this._age = val;
}
get age() {
return this._age;
}
}
const p = Person.create('张三', 25);
console.log(p.greet()); // 你好,我是 张三,今年 25 岁。
console.log(Person.prototype.greet === p.greet); // true ------ 方法共享于原型
console.log(typeof Person); // 'function'
【代码注释】 class 是构造函数的语法糖:类方法写在原型上(所有实例共享),static 写在类本身,constructor 是构造入口。用 class 而非手写原型链,代码可读性与可维护性大幅提升。
3.10.2 继承(extends / super)
javascript
// 【代码注释】extends:子类继承父类所有实例方法和静态方法
// super() 必须在子类 constructor 的 this 使用之前调用,否则 ReferenceError
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} 发出了声音。`;
}
static kingdom() {
return '动物界';
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 先调用父类 constructor,初始化 this.name
this.breed = breed; // 再添加子类独有属性
}
// 方法覆盖(Override):子类同名方法遮蔽父类,可用 super.speak() 调用父类版本
speak() {
return `${super.speak()} 汪汪!`;
}
// 子类新方法
fetch() {
return `${this.name}(${this.breed})正在捡球...`;
}
}
const dog = new Dog('旺财', '柴犬');
console.log(dog.speak()); // 旺财 发出了声音。 汪汪!
console.log(Dog.kingdom()); // 动物界 ------ 静态方法也可继承
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true ------ 原型链包含 Animal.prototype
【代码注释】 extends 同时建立实例链(Dog.prototype → Animal.prototype)和静态链(Dog → Animal);super() 调用父类构造函数;super.method() 访问父类方法。经典用途:框架自定义错误类、React 组件类。
3.10.3 私有字段(#)与静态私有
javascript
// 【代码注释】私有字段(ES2022):# 前缀,严格限于类内部访问,类外访问 SyntaxError
class BankAccount {
#balance = 0; // 私有实例字段,外部无法读写
#transactionLog = []; // 每个实例都有独立的日志数组
static #interestRate = 0.035; // 静态私有字段,整个类共享
constructor(initialBalance) {
this.#balance = initialBalance;
this.#log('开户', initialBalance);
}
deposit(amount) {
if (amount <= 0) throw new Error('存款金额须为正数');
this.#balance += amount;
this.#log('存款', amount);
return this; // 支持链式调用
}
withdraw(amount) {
if (amount > this.#balance) throw new Error('余额不足');
this.#balance -= amount;
this.#log('取款', amount);
return this;
}
// 公有 getter 只读暴露余额,外部无法 account.#balance = 999
get balance() {
return this.#balance;
}
get statement() {
return this.#transactionLog.slice(); // 返回副本,防止外部修改日志
}
// 私有方法:只在类内部使用,外部不可调用
#log(type, amount) {
this.#transactionLog.push({
type,
amount,
balance: this.#balance,
time: new Date().toLocaleTimeString()
});
}
static calculateAnnualReturn(principal) {
return principal * BankAccount.#interestRate; // 类内可访问静态私有字段
}
}
const account = new BankAccount(1000);
account.deposit(500).withdraw(200); // 链式调用
console.log(account.balance); // 1300
console.log(account.statement); // 交易记录数组
// account.#balance = 99999; // SyntaxError(私有字段)
【代码注释】 私有字段(#)是语言级封装:不是约定俗成的 _ 前缀,而是真正的访问控制,外部访问 SyntaxError。适合银行账户、密码哈希、内部计数器等不应暴露的敏感状态。
3.10.4 Mixin 模式(多继承模拟)
javascript
// 【代码注释】JavaScript 单继承限制:extends 只能一个父类。Mixin 用函数返回类的技巧绕过此限制
// Mixin:接受基类,返回扩展后的新类
const Serializable = (Base) => class extends Base {
serialize() {
return JSON.stringify(this);
}
static deserialize(json) {
return Object.assign(new this(), JSON.parse(json));
}
};
const Validatable = (Base) => class extends Base {
validate() {
const errors = [];
for (const [field, rule] of Object.entries(this.constructor.validationRules || {})) {
if (rule.required && !this[field]) {
errors.push(`${field} 是必填项`);
}
if (rule.minLength && this[field]?.length < rule.minLength) {
errors.push(`${field} 最少 ${rule.minLength} 个字符`);
}
}
return { valid: errors.length === 0, errors };
}
};
class BaseModel {
constructor(data = {}) {
Object.assign(this, data);
}
}
// 组合多个 Mixin ------ 从右到左叠加 extends
class User extends Serializable(Validatable(BaseModel)) {
static validationRules = {
name: { required: true, minLength: 2 },
email: { required: true }
};
}
const user = new User({ name: '张三', email: 'zs@example.com' });
console.log(user.validate()); // { valid: true, errors: [] }
console.log(user.serialize()); // '{"name":"张三","email":"zs@example.com"}'
const badUser = new User({ name: 'A' });
console.log(badUser.validate()); // { valid: false, errors: ['email 是必填项', 'name 最少 2 个字符'] }
【代码注释】 Mixin 模式解决 JS 单继承局限:每个 Mixin 是「接受基类,返回扩展类」的高阶函数,可组合 N 个能力而不影响原有继承链。Vue 2 mixins 选项、React withXxx HOC 都是变体形式。
3.11 Iterator 与 Generator
3.11.1 可迭代协议(Iterable Protocol)
名词解析:
- Iterator(迭代器) :有
next()方法的对象,每次调用返回{ value, done } - Iterable(可迭代对象) :有
[Symbol.iterator]()方法的对象,for...of会自动调用它
javascript
// 【代码注释】可迭代协议:实现 Symbol.iterator 即可接入 for...of / 展开 / 解构 / Array.from
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
// 实现迭代器工厂:每次 for...of 调用此方法得到新迭代器
[Symbol.iterator]() {
let current = this.start;
const { end, step } = this;
return {
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true }; // done:true 告知循环结束
}
};
}
}
const range = new Range(1, 10, 2);
for (const n of range) {
console.log(n); // 1 3 5 7 9
}
console.log([...range]); // [1, 3, 5, 7, 9]
const [first, second] = range; // 解构也走迭代协议
console.log(first, second); // 1 3
console.log(Array.from(range)); // [1, 3, 5, 7, 9]
【代码注释】 实现 [Symbol.iterator]() 后,该类的实例可用于 for...of、展开 ...、解构、Array.from。自定义迭代器的典型应用:分页游标、树形遍历、无限数列。
3.11.2 Generator 函数
javascript
// 【代码注释】Generator 函数(function*):每次执行到 yield 暂停,next() 恢复执行
// 返回值是一个同时满足迭代器和可迭代协议的对象
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr; // 暂停,输出 curr;调用 next() 从此处恢复
[prev, curr] = [curr, prev + curr];
}
}
// 取前 10 个斐波那契数
const fib = fibonacci();
const first10 = Array.from({ length: 10 }, () => fib.next().value);
console.log(first10); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// 【代码注释】有限 Generator:自动结束,done:true 后续 next() 均返回 undefined
function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
for (const n of range(0, 10, 3)) {
console.log(n); // 0 3 6 9
}
// 【代码注释】yield* 委托给另一个可迭代对象(Generator 或数组)
function* flatten(arr) {
for (const item of arr) {
if (Array.isArray(item)) {
yield* flatten(item); // 递归展开,等价于对子数组逐一 yield
} else {
yield item;
}
}
}
console.log([...flatten([1, [2, [3, [4]]], 5])]); // [1, 2, 3, 4, 5]
【代码注释】 Generator 函数的核心价值:惰性求值 (按需生成,不提前计算全部结果),适合无限数列、大数据集逐行处理、协程式异步控制流(早期 co 库)。
3.11.3 Generator 经典应用场景
javascript
// 【代码注释】场景一:分页游标 ------ 每次请求只在调用 next() 时发出,天然惰性
async function* paginate(url, pageSize = 20) {
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(`${url}?page=${page}&size=${pageSize}`);
const { data, total } = await response.json();
yield data; // 每次 yield 一页数据
hasMore = page * pageSize < total;
page++;
}
}
// 调用方代码(异步 for...of)
async function loadAllUsers() {
const allUsers = [];
for await (const page of paginate('/api/users')) {
allUsers.push(...page);
console.log(`已加载 ${allUsers.length} 个用户`);
}
return allUsers;
}
// 【代码注释】场景二:ID 生成器 ------ 每个模块独立维护自增序列,无全局状态污染
function* idGenerator(prefix = 'ID') {
let id = 1;
while (true) {
yield `${prefix}-${String(id++).padStart(6, '0')}`;
}
}
const userIdGen = idGenerator('USER');
const orderIdGen = idGenerator('ORDER');
console.log(userIdGen.next().value); // USER-000001
console.log(userIdGen.next().value); // USER-000002
console.log(orderIdGen.next().value); // ORDER-000001
// 【代码注释】场景三:状态机 ------ 每个 yield 是一个状态节点,next(input) 传入触发条件
function* trafficLight() {
while (true) {
yield '🔴 红灯(停车)';
yield '🟡 黄灯(准备)';
yield '🟢 绿灯(通行)';
}
}
const light = trafficLight();
setInterval(() => {
console.log(light.next().value);
}, 2000); // 每 2s 切换一次灯
【代码注释】 Generator 三大应用:分页游标(异步惰性加载)、唯一 ID 生成器(闭包维护状态)、有限状态机(每个 yield 即一个状态)。配合 async function* + for await...of 实现异步迭代流。
3.12 Proxy 与 Reflect
3.12.1 Proxy 基本用法
名词解析:
- Proxy(代理):拦截对目标对象的操作(读/写/删除/函数调用等),可插入自定义逻辑
- Reflect:提供与 Proxy 陷阱一一对应的默认行为方法,确保标准操作不被遗漏
javascript
// 【代码注释】Proxy(target, handler):target 是被代理对象,handler 定义拦截行为(陷阱 trap)
const user = { name: '张三', age: 25, _password: 'secret' };
const safeUser = new Proxy(user, {
// get 陷阱:读取属性时触发
get(target, prop, receiver) {
if (prop.startsWith('_')) {
throw new Error(`禁止访问私有属性:${prop}`);
}
console.log(`[读取] ${prop}`);
return Reflect.get(target, prop, receiver); // 用 Reflect 执行默认读取
},
// set 陷阱:赋值时触发
set(target, prop, value, receiver) {
if (prop === 'age') {
if (typeof value !== 'number' || value < 0 || value > 150) {
throw new TypeError(`age 值非法:${value}`);
}
}
console.log(`[写入] ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver); // 返回 true 表示成功
},
// has 陷阱:in 运算符触发
has(target, prop) {
if (prop.startsWith('_')) return false; // 屏蔽私有属性的存在
return Reflect.has(target, prop);
},
// deleteProperty 陷阱
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error(`禁止删除私有属性:${prop}`);
}
return Reflect.deleteProperty(target, prop);
}
});
console.log(safeUser.name); // [读取] name → 张三
safeUser.age = 30; // [写入] age = 30
// safeUser._password; // Error: 禁止访问私有属性:_password
// safeUser.age = -1; // TypeError: age 值非法
console.log('_password' in safeUser); // false(has 陷阱屏蔽)
【代码注释】 Proxy 的核心是透明拦截 :配合 Reflect 保留默认行为,在之上叠加校验/日志/访问控制。Vue 3 响应式系统(reactive)正是基于 Proxy + Reflect,取代了 Vue 2 的 Object.defineProperty。
3.12.2 Proxy 实战:响应式数据
javascript
// 【代码注释】简版响应式系统:track(依赖收集)+ trigger(触发更新),模拟 Vue 3 核心机制
function reactive(target) {
const listeners = new Map(); // prop → Set<callback>
return new Proxy(target, {
get(obj, prop) {
// 依赖收集:当前正在执行的 effect 订阅此属性
if (currentEffect) {
if (!listeners.has(prop)) listeners.set(prop, new Set());
listeners.get(prop).add(currentEffect);
}
const value = Reflect.get(obj, prop);
// 嵌套对象递归代理(Vue 3 lazy 实现)
return typeof value === 'object' && value !== null
? reactive(value)
: value;
},
set(obj, prop, value) {
const result = Reflect.set(obj, prop, value);
// 触发所有订阅此属性的 effect
if (listeners.has(prop)) {
listeners.get(prop).forEach(fn => fn());
}
return result;
}
});
}
let currentEffect = null;
function effect(fn) {
currentEffect = fn;
fn(); // 首次执行收集依赖
currentEffect = null;
}
// 使用示例
const state = reactive({ count: 0, name: '张三' });
effect(() => {
console.log(`[视图更新] count = ${state.count}`); // 订阅 count
});
state.count++; // → [视图更新] count = 1
state.count++; // → [视图更新] count = 2
state.name = '李四'; // 无输出(effect 只订阅了 count)
【代码注释】 这 40 行代码还原了 Vue 3 reactive + effect 的核心思路:get 收集当前 effect 为依赖,set 触发相关 effect 重跑。理解此模式后再看 Vue 3 源码会豁然开朗。
3.12.3 Proxy 其他常见应用
javascript
// 【代码注释】应用一:验证对象(表单数据入口)
function createValidatedObject(validationSchema) {
return new Proxy({}, {
set(target, prop, value) {
const rule = validationSchema[prop];
if (rule) {
if (rule.type && typeof value !== rule.type) {
throw new TypeError(`${prop} 必须是 ${rule.type} 类型`);
}
if (rule.min !== undefined && value < rule.min) {
throw new RangeError(`${prop} 不能小于 ${rule.min}`);
}
if (rule.max !== undefined && value > rule.max) {
throw new RangeError(`${prop} 不能大于 ${rule.max}`);
}
if (rule.pattern && !rule.pattern.test(value)) {
throw new Error(`${prop} 格式不正确`);
}
}
return Reflect.set(target, prop, value);
}
});
}
const userForm = createValidatedObject({
age: { type: 'number', min: 0, max: 150 },
email: { type: 'string', pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
name: { type: 'string' }
});
userForm.name = '张三'; // ✅
userForm.email = 'zs@example.com'; // ✅
// userForm.age = -1; // RangeError
// userForm.email = 'invalid-email'; // Error: email 格式不正确
// 【代码注释】应用二:函数调用日志(函数 Proxy,apply 陷阱)
function withCallLog(fn, name = fn.name) {
return new Proxy(fn, {
apply(target, thisArg, args) {
const start = performance.now();
const result = Reflect.apply(target, thisArg, args);
const cost = (performance.now() - start).toFixed(2);
console.log(`[${name}] 参数:`, args, '耗时:', `${cost}ms`, '返回:', result);
return result;
}
});
}
const slowAdd = withCallLog((a, b) => {
// 模拟耗时
const end = Date.now() + 10;
while (Date.now() < end) {}
return a + b;
}, 'slowAdd');
slowAdd(3, 4); // [slowAdd] 参数: [3, 4] 耗时: ~10ms 返回: 7
【代码注释】 Proxy 的两大高频场景:数据验证 (set 陷阱做入参校验,替代手写 setter)和函数增强(apply 陷阱做 AOP 日志/计时),均无侵入性,不修改原对象/函数。
3.13 可选链与空值合并
3.13.1 可选链(Optional Chaining ?.)
javascript
// 【代码注释】可选链 ?. :短路求值,左侧为 null/undefined 时直接返回 undefined,不抛错
const user = {
name: '张三',
address: {
city: '北京',
geo: { lat: 39.9, lng: 116.4 }
},
// phone: undefined // 字段不存在
};
// ❌ 旧写法:冗余的防护链(易遗漏导致 Cannot read property of undefined)
const lat = user && user.address && user.address.geo && user.address.geo.lat;
// ✅ 可选链:任一环节为 null/undefined 则整体返回 undefined
const lat2 = user?.address?.geo?.lat; // 39.9
const phone = user?.phone?.number; // undefined(不抛错)
const zipCode = user?.address?.zipCode; // undefined
// 可选下标访问:数组或 null 的动态下标
const firstTag = user?.tags?.[0]; // undefined
const result = user?.tags?.find(t => t.id === 1); // undefined
// 可选方法调用
const formatted = user?.getFullName?.(); // undefined(方法不存在)
const upperName = user?.name?.toUpperCase(); // '张三'
// 【代码注释】结合解构与默认值
const { address: { city = '未知城市' } = {} } = user ?? {};
【代码注释】 可选链 ?. 大幅减少防御式判断代码,尤其适合深层嵌套的 API 响应解析、DOM 属性访问、可选回调调用。注意:?. 只对 null/undefined 短路,0、''、false 不触发。
3.13.2 空值合并(Nullish Coalescing ??)
javascript
// 【代码注释】?? 与 || 的关键区别:
// || falsy 即回退(含 0、''、false、NaN)
// ?? 仅 null/undefined 回退,保留合法的「假值」
function createConfig(options = {}) {
return {
// ❌ 用 || 时:timeout=0(禁用超时)会被误判为假值而用默认值
timeoutOld: options.timeout || 5000, // 若 timeout=0 → 5000(错误)
// ✅ 用 ?? 时:只在 null/undefined 时回退
timeout: options.timeout ?? 5000, // 若 timeout=0 → 0(正确)
debug: options.debug ?? false, // false 保留
maxRetries: options.maxRetries ?? 3,
// 可选链 + 空值合并组合:从深层取值,取不到时给默认值
logLevel: options?.logger?.level ?? 'info'
};
}
console.log(createConfig({ timeout: 0, debug: false }));
// { timeout: 0, debug: false, maxRetries: 3, logLevel: 'info' }
// 【代码注释】逻辑赋值运算符(ES2021)------ 惰性赋值,仅在条件满足时执行赋值
let a = null;
let b = '';
let c = 0;
a ??= '默认值'; // 等价 a = a ?? '默认值':a 为 null → 赋值
b ||= '备用值'; // 等价 b = b || '备用值':'' 为 falsy → 赋值
c &&= c * 2; // 等价 c = c && c * 2:0 为 falsy → 不赋值
console.log(a, b, c); // '默认值' '备用值' 0
// 【代码注释】真实场景:组件默认 props 处理
function initComponent(props) {
// 仅当 props 中真正没有该字段(null/undefined)才使用默认值
props.theme ??= 'light';
props.pageSize ??= 20;
props.disabled ??= false; // props.disabled = false 时不会被覆盖
return props;
}
【代码注释】 ?? 与 || 是不同问题的工具:|| 用于「有值就用,空就回退」,?? 用于「明确缺失(null/undefined)才回退」。在处理数字 0、空字符串 ''、false 等合法假值时必须用 ??,否则会产生难以察觉的 bug。
4. 数组操作深度解析
4.1 数组扁平化技术
名词解析:数组扁平化(Array Flattening)
数组扁平化是指将多维嵌套数组转换为一维数组的过程,也称为数组拉平。
扁平化方法对比:
javascript
// 【代码注释】测试数据:混合 number、string、多层嵌套数组,用于对比各扁平化策略的行为差异
const nestedArray = [
[1000, 2000, 3000],
'hello',
[
[10, 20, 30],
[
'a',
'b',
['A', 'B', 'C']
],
'嵌套字符串'
],
12313,
[101, 202, 303]
];
console.log('原始数组:', nestedArray);
// 递归求最大嵌套深度:非数组返回 0,数组为 1 + 子项深度最大值
function getArrayDepth(arr) {
return Array.isArray(arr) ?
1 + Math.max(0, ...arr.map(getArrayDepth)) : 0;
}
console.log('数组维度:', getArrayDepth(nestedArray));
// 【代码注释】方法一:手写递归 ------ depth 控制「最多展开几层」,Infinity 表示完全扁平
function flatArrayRecursive(arr, depth = Infinity) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) && depth > 0) {
result = result.concat(flatArrayRecursive(arr[i], depth - 1));
} else {
result.push(arr[i]); // 已达深度上限或非数组,原样保留(可能仍为嵌套数组)
}
}
return result;
}
console.log('递归扁平化(无限深度):', flatArrayRecursive(nestedArray));
console.log('递归扁平化(深度2):', flatArrayRecursive(nestedArray, 2));
// 【代码注释】方法二:toString + split ------ 依赖 Array#toString 逗号连接,仅适合纯数字/字符串
function flatArrayToString(arr) {
return arr.toString().split(',');
}
console.log('字符串方法扁平化:', flatArrayToString(nestedArray));
// 缺陷:元素变 string;含逗号的字符串会被错误切分;无法指定 depth
// 【代码注释】方法三:Array.prototype.flat(depth) ------ ES2019,引擎原生实现,生产首选
function flatArrayNative(arr, depth = Infinity) {
return arr.flat(depth);
}
console.log('原生方法扁平化:', flatArrayNative(nestedArray));
console.log('原生方法扁平化(深度1):', flatArrayNative(nestedArray, 1));
// 【代码注释】方法四:reduce + 递归 ------ 函数式风格,语义与手写 for 循环等价
function flatArrayReduce(arr, depth = Infinity) {
return arr.reduce((acc, val) => {
if (Array.isArray(val) && depth > 0) {
return acc.concat(flatArrayReduce(val, depth - 1));
} else {
return acc.concat(val);
}
}, []);
}
console.log('Reduce方法扁平化:', flatArrayReduce(nestedArray));
【代码注释】 扁平化性能对比:原生 flat(Infinity) 利用引擎内部优化,速度最快;递归方法可读性最佳;toString().split(',') 会丢失原始类型,仅适合纯字符串数组。
4.2 扁平化性能对比
性能测试代码:
javascript
// 【代码注释】微基准模板:同一输入、多函数对比耗时;注意 JIT 预热,正式测试宜多次取中位数
function performanceTest(methods, testArray) {
const results = {};
methods.forEach(method => {
const start = performance.now();
const result = method.fn(testArray);
const end = performance.now();
results[method.name] = {
time: end - start,
result: result.length
};
});
return results;
}
const largeNestedArray = Array(1000).fill(0).map((_, i) => [
i,
[i * 2, [i * 3, [i * 4]]]
]);
const methods = [
{ name: '递归方法', fn: (arr) => flatArrayRecursive(arr) },
{ name: '原生flat', fn: (arr) => arr.flat(Infinity) },
{ name: 'Reduce方法', fn: (arr) => flatArrayReduce(arr) }
];
const performanceResults = performanceTest(methods, largeNestedArray);
console.log('性能测试结果:', performanceResults);
【代码注释】 performance.now() 精度达微秒级,适合微基准测试;大型嵌套数组(1000 项 × 4 层)下 flat(Infinity) 通常比手写递归快 2~5 倍。
4.3 数组浅拷贝技术
名词解析:浅拷贝(Shallow Copy)
浅拷贝只复制对象或数组的第一层属性,嵌套的对象或数组仍然共享原始引用。
浅拷贝方法详解:
javascript
// 【代码注释】浅拷贝:只复制容器第一层;元素若为对象/数组,仍与源共享同一引用
const originalArray = [
{ id: 1, name: '商品A', price: 100 },
{ id: 2, name: '商品B', price: 200 },
{ id: 3, name: '商品C', price: 300 }
];
const originalObject = {
userId: 123,
username: '张三',
profile: { age: 25, city: '北京' },
orders: ['订单1', '订单2']
};
// 方法一:[...arr] / { ...obj } ------ ES6 惯用写法,简洁且不改原结构
const arrayCopySpread = [...originalArray];
const objectCopySpread = { ...originalObject };
arrayCopySpread[0].price = 999;
console.log('原始数组受影响:', originalArray[0].price); // 999 ------ 嵌套对象未复制
// 方法二:concat() ------ 不传参时等价浅拷贝数组;push 只影响新数组长度
const arrayCopyConcat = originalArray.concat();
arrayCopyConcat.push({ id: 4, name: '商品D', price: 400 });
console.log('concat拷贝长度:', arrayCopyConcat.length); // 4
console.log('原数组长度:', originalArray.length); // 3
// 方法三:slice() ------ slice(0) 或 slice() 复制整个数组,同样为浅拷贝
const arrayCopySlice = originalArray.slice();
// 方法四:Object.assign({}, src) ------ 将 src 自有可枚举属性复制到目标对象
const objectCopyAssign = Object.assign({}, originalObject);
// 与展开区别:assign 会调用目标/源上的 setter;展开是属性描述符级别拷贝
// 方法五:Array.from(iterable, mapFn) ------ 可在拷贝同时映射变换
const arrayCopyFrom = Array.from(originalArray);
const mappedCopy = Array.from(originalArray, item => ({
...item, // 对每个元素再浅展开,得到新对象引用
price: item.price * 1.1
}));
【代码注释】 浅拷贝五种方式对比:[...arr] 和 Object.assign 最常用;Array.from 支持 mapper;concat/slice 仅限数组。核心:嵌套引用仍共享,修改深层属性会影响原始数据。
4.4 实战应用:购物车系统
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数组扁平化与拷贝实战</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}
.section {
margin: 20px 0;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.section h2 {
color: #667eea;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.button {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.button:hover {
background: #764ba2;
transform: translateY(-2px);
}
.button:active {
transform: translateY(0);
}
.result {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-top: 15px;
font-family: 'Courier New', monospace;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}
.product-item {
background: #fff;
border: 1px solid #ddd;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.product-info {
flex: 1;
}
.product-price {
color: #e74c3c;
font-weight: bold;
}
.nested-example {
background: #f0f0f0;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>🛒 数组扁平化与拷贝技术实战</h1>
<div class="section">
<h2>📊 多维数组扁平化演示</h2>
<button class="button" onclick="demonstrateFlattening()">演示扁平化</button>
<button class="button" onclick="testFlatteningMethods()">性能测试</button>
<div id="flattenResult" class="result"></div>
</div>
<div class="section">
<h2>🛍️ 购物车浅拷贝演示</h2>
<button class="button" onclick="addToCart()">添加商品</button>
<button class="button" onclick="copyCart()">拷贝购物车</button>
<button class="button" onclick="modifyCopy()">修改拷贝</button>
<button class="button" onclick="compareCarts()">比较购物车</button>
<div id="cartResult"></div>
<div id="cartComparison" class="result"></div>
</div>
<div class="section">
<h2>📈 数据处理管道</h2>
<button class="button" onclick="processData()">处理数据</button>
<div id="pipelineResult" class="result"></div>
</div>
</div>
<script>
// 【代码注释】扁平化演示区:nestedData 为分类+商品列表的二维业务结构(本页未直接用于 flat 按钮)
const nestedData = [
{
category: '电子产品',
items: [
{ name: '手机', price: 2999, specs: ['8GB', '128GB', '5G'] },
{ name: '笔记本', price: 5999, specs: ['16GB', '512GB', 'i7'] }
]
},
{
category: '配件',
items: [
{ name: '耳机', price: 299, specs: ['蓝牙', '降噪'] },
{ name: '充电器', price: 99, specs: ['快充', '多口'] }
]
}
];
// 【代码注释】浅拷贝演示区:每项为对象,拷贝数组后改 item.price 会联动原购物车
let shoppingCart = [
{
id: 1,
name: 'iPhone 15',
price: 5999,
quantity: 1,
specs: { storage: '128GB', color: '蓝色' }
},
{
id: 2,
name: 'AirPods Pro',
price: 1899,
quantity: 2,
specs: { type: '入耳式', noise: '主动降噪' }
}
];
let cartCopy = null;
// 【代码注释】对比 flat(depth):depth 为 1 只展开最外层子数组,Infinity 递归至一维
function demonstrateFlattening() {
const resultDiv = document.getElementById('flattenResult');
const testArray = [
[1, 2, 3],
[[4, 5], [6, 7]],
[[[8, 9]], 10]
];
const results = {
'原始数组': JSON.stringify(testArray, null, 2),
'flat(1) 一层扁平': testArray.flat(1),
'flat(2) 两层扁平': testArray.flat(2),
'flat(Infinity) 完全扁平': testArray.flat(Infinity)
};
resultDiv.textContent = JSON.stringify(results, null, 2);
}
// 【代码注释】微基准:1000 条三层嵌套,对比手写递归 / flat / toString 耗时
function testFlatteningMethods() {
const resultDiv = document.getElementById('flattenResult');
// 创建大型嵌套数组
const largeArray = Array(1000).fill(0).map((_, i) => [
i,
[i * 2, [i * 3]]
]);
const methods = [
{
name: '递归方法',
fn: (arr) => {
const result = [];
function flatten(item) {
if (Array.isArray(item)) {
item.forEach(flatten);
} else {
result.push(item);
}
}
arr.forEach(flatten);
return result;
}
},
{
name: '原生flat',
fn: (arr) => arr.flat(Infinity)
},
{
name: 'toString + split',
fn: (arr) => arr.toString().split(',')
}
];
const performanceData = {};
methods.forEach(method => {
const start = performance.now();
const result = method.fn(largeArray);
const end = performance.now();
performanceData[method.name] = {
执行时间: `${(end - start).toFixed(2)}ms`,
结果长度: result.length,
性能: end - start < 1 ? '优秀' : end - start < 10 ? '良好' : '一般'
};
});
resultDiv.textContent = JSON.stringify(performanceData, null, 2);
}
// 【代码注释】添加商品到购物车
function addToCart() {
const newProduct = {
id: shoppingCart.length + 1,
name: `产品${shoppingCart.length + 1}`,
price: Math.floor(Math.random() * 5000) + 1000,
quantity: 1,
specs: {
storage: '256GB',
color: '随机'
}
};
shoppingCart.push(newProduct);
displayCart();
}
function copyCart() {
cartCopy = [...shoppingCart]; // 新数组,但元素仍是同一批对象引用
alert(`购物车已拷贝!包含 ${cartCopy.length} 个商品`);
}
function modifyCopy() {
if (!cartCopy) {
alert('请先拷贝购物车!');
return;
}
// 修改的是 item 对象本身,shoppingCart 与 cartCopy 指向相同 item
cartCopy.forEach(item => {
item.price = Math.floor(item.price * 0.9);
});
alert('拷贝购物车的所有商品已打9折!(原购物车价格也会变)');
}
function compareCarts() {
if (!cartCopy) {
alert('请先拷贝购物车!');
return;
}
const comparison = {
'原购物车商品': shoppingCart.map(item => ({
name: item.name,
price: item.price
})),
'拷贝购物车商品': cartCopy.map(item => ({
name: item.name,
price: item.price
})),
'说明': '浅拷贝的对象属性共享引用,修改拷贝中的对象属性会影响原数组'
};
document.getElementById('cartComparison').textContent =
JSON.stringify(comparison, null, 2);
}
// 【代码注释】显示购物车内容
function displayCart() {
const cartDiv = document.getElementById('cartResult');
if (shoppingCart.length === 0) {
cartDiv.innerHTML = '<p>购物车为空</p>';
return;
}
const total = shoppingCart.reduce((sum, item) =>
sum + (item.price * item.quantity), 0);
cartDiv.innerHTML = shoppingCart.map(item => `
<div class="product-item">
<div class="product-info">
<strong>${item.name}</strong><br>
<small>规格: ${item.specs.storage} / ${item.specs.color}</small>
</div>
<div class="product-price">
¥${item.price} x ${item.quantity}
</div>
</div>
`).join('') + `
<div style="text-align: right; margin-top: 20px;">
<strong>总计: ¥${total}</strong>
</div>
`;
}
// 【代码注释】数据处理管道演示
function processData() {
const resultDiv = document.getElementById('pipelineResult');
// 模拟API返回的嵌套数据
const apiResponse = {
status: 'success',
data: {
users: [
{
id: 1,
name: '张三',
orders: [
{ orderId: 'A001', amount: 100 },
{ orderId: 'A002', amount: 200 }
]
},
{
id: 2,
name: '李四',
orders: [
{ orderId: 'B001', amount: 150 }
]
}
]
}
};
// flatMap = map + flat(1):每个 user 映射为订单数组后压平为一维列表
const processedData = {
'原始数据': apiResponse,
'提取订单数组': apiResponse.data.users.flatMap(user =>
user.orders.map(order => ({
...order,
userName: user.name,
userId: user.id
}))
),
'订单总金额': apiResponse.data.users
.flatMap(user => user.orders)
.reduce((sum, order) => sum + order.amount, 0)
};
resultDiv.textContent = JSON.stringify(processedData, null, 2);
}
// 初始化显示
displayCart();
</script>
</body>
</html>
【代码注释】 完整可运行 HTML:购物车浅拷贝演示展示「修改拷贝中的对象属性影响原数组」的经典现象;flatMap + reduce 数据管道演示嵌套订单数据的提取与聚合。
5. 对象属性特性详解
5.1 属性描述符架构
名词解析:属性描述符(Property Descriptor)
属性描述符是一个对象,用于描述对象属性的各种特性和配置。它决定了属性的行为方式,包括是否可写、可枚举、可配置等。
属性描述符完整架构:
#mermaid-svg-2AWQq6NMg06ISp1v{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-2AWQq6NMg06ISp1v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2AWQq6NMg06ISp1v .error-icon{fill:#552222;}#mermaid-svg-2AWQq6NMg06ISp1v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2AWQq6NMg06ISp1v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2AWQq6NMg06ISp1v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2AWQq6NMg06ISp1v .marker.cross{stroke:#333333;}#mermaid-svg-2AWQq6NMg06ISp1v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2AWQq6NMg06ISp1v p{margin:0;}#mermaid-svg-2AWQq6NMg06ISp1v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2AWQq6NMg06ISp1v .cluster-label text{fill:#333;}#mermaid-svg-2AWQq6NMg06ISp1v .cluster-label span{color:#333;}#mermaid-svg-2AWQq6NMg06ISp1v .cluster-label span p{background-color:transparent;}#mermaid-svg-2AWQq6NMg06ISp1v .label text,#mermaid-svg-2AWQq6NMg06ISp1v span{fill:#333;color:#333;}#mermaid-svg-2AWQq6NMg06ISp1v .node rect,#mermaid-svg-2AWQq6NMg06ISp1v .node circle,#mermaid-svg-2AWQq6NMg06ISp1v .node ellipse,#mermaid-svg-2AWQq6NMg06ISp1v .node polygon,#mermaid-svg-2AWQq6NMg06ISp1v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2AWQq6NMg06ISp1v .rough-node .label text,#mermaid-svg-2AWQq6NMg06ISp1v .node .label text,#mermaid-svg-2AWQq6NMg06ISp1v .image-shape .label,#mermaid-svg-2AWQq6NMg06ISp1v .icon-shape .label{text-anchor:middle;}#mermaid-svg-2AWQq6NMg06ISp1v .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2AWQq6NMg06ISp1v .rough-node .label,#mermaid-svg-2AWQq6NMg06ISp1v .node .label,#mermaid-svg-2AWQq6NMg06ISp1v .image-shape .label,#mermaid-svg-2AWQq6NMg06ISp1v .icon-shape .label{text-align:center;}#mermaid-svg-2AWQq6NMg06ISp1v .node.clickable{cursor:pointer;}#mermaid-svg-2AWQq6NMg06ISp1v .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2AWQq6NMg06ISp1v .arrowheadPath{fill:#333333;}#mermaid-svg-2AWQq6NMg06ISp1v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2AWQq6NMg06ISp1v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2AWQq6NMg06ISp1v .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2AWQq6NMg06ISp1v .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2AWQq6NMg06ISp1v .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2AWQq6NMg06ISp1v .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2AWQq6NMg06ISp1v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2AWQq6NMg06ISp1v .cluster text{fill:#333;}#mermaid-svg-2AWQq6NMg06ISp1v .cluster span{color:#333;}#mermaid-svg-2AWQq6NMg06ISp1v 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-2AWQq6NMg06ISp1v .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2AWQq6NMg06ISp1v rect.text{fill:none;stroke-width:0;}#mermaid-svg-2AWQq6NMg06ISp1v .icon-shape,#mermaid-svg-2AWQq6NMg06ISp1v .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2AWQq6NMg06ISp1v .icon-shape p,#mermaid-svg-2AWQq6NMg06ISp1v .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2AWQq6NMg06ISp1v .icon-shape .label rect,#mermaid-svg-2AWQq6NMg06ISp1v .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2AWQq6NMg06ISp1v .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2AWQq6NMg06ISp1v .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2AWQq6NMg06ISp1v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 属性描述符
数据属性
访问器属性
value - 属性值
writable - 可写性
enumerable - 可枚举性
configurable - 可配置性
get - getter函数
set - setter函数
enumerable - 可枚举性
configurable - 可配置性
存储具体数据值
动态计算属性值
【代码注释】 属性描述符双轨架构:数据属性(value/writable)存值;访问器属性(get/set)动态计算。二者均受 enumerable/configurable 控制,不可同时指定 value 和 get。
5.2 数据属性详解
数据属性的四大特性:
javascript
// 【代码注释】属性描述符:精确控制单个属性的读写、遍历、删除、重定义行为
const product = {
id: 1001,
name: '高端笔记本电脑',
price: 5999,
category: '电子产品'
};
// 普通字面量属性默认为数据描述符:writable/enumerable/configurable 均为 true
const descriptors = Object.getOwnPropertyDescriptors(product);
console.log('product对象的属性描述符:', descriptors);
// 【代码注释】defineProperty 新增或重定义属性;与字面量不同,可显式设为只读/不可枚举
Object.defineProperty(product, 'serialNumber', {
value: 'SN20240115001',
writable: false, // 赋值无效(严格模式 TypeError,非严格静默失败)
enumerable: true, // 会出现在 for...in / Object.keys
configurable: false // 不能 delete,不能再次修改描述符(除 value 在 writable:true 时)
});
product.serialNumber = 'NEW_SERIAL';
console.log('序列号(修改后):', product.serialNumber); // 仍为 SN20240115001
Object.defineProperty(product, 'internalCode', {
value: 'INT-12345',
writable: true,
enumerable: false, // 对 keys/for...in 隐藏,常用于内部状态字段
configurable: true
});
console.log('可枚举属性:', Object.keys(product));
console.log('所有自有属性名:', Object.getOwnPropertyNames(product)); // 含 internalCode
【代码注释】 Object.defineProperty 精确控制属性行为:writable:false 只读;enumerable:false 隐藏于 Object.keys/for...in;configurable:false 防删防重配。Vue 2 响应式正基于此机制。
5.3 访问器属性实战应用
访问器属性的高级应用场景:
javascript
// 【代码注释】访问器属性(Accessor):对外暴露「属性」语法,内部用 _ 前缀字段存真实数据
class SmartProduct {
constructor(basePrice, discountRate = 0) {
this._basePrice = basePrice;
this._discountRate = discountRate;
this._salesCount = 0;
}
// getter:无参,每次访问时按当前 _discountRate 重新计算,避免维护冗余字段
get finalPrice() {
const discount = this._basePrice * (this._discountRate / 100);
return Math.round(this._basePrice - discount);
}
// setter:赋值 laptop.discountRate = x 时触发,可做范围校验(业务不变式)
set discountRate(rate) {
if (rate < 0 || rate > 100) {
throw new Error('折扣率必须在0-100之间');
}
this._discountRate = rate;
}
get discountRate() {
return this._discountRate;
}
get dynamicPrice() {
const bulkDiscount = Math.min(this._salesCount * 0.01, 0.2);
return Math.round(this.finalPrice * (1 - bulkDiscount));
}
recordSale() {
this._salesCount++;
}
// 只读聚合视图:无对应 setter,外部无法 productInfo = {...}
get productInfo() {
return {
basePrice: this._basePrice,
finalPrice: this.finalPrice,
discountRate: this._discountRate,
salesCount: this._salesCount,
dynamicPrice: this.dynamicPrice
};
}
}
const laptop = new SmartProduct(5999, 10);
console.log('产品信息:', laptop.productInfo);
laptop.recordSale();
laptop.recordSale();
console.log('销售2次后的动态价格:', laptop.dynamicPrice);
laptop.discountRate = 15; // 走 setter,而非直接改 _discountRate
console.log('调整折扣率后的价格:', laptop.finalPrice);
【代码注释】 访问器属性实战:get finalPrice 每次访问时动态计算,无需手动维护派生值;set discountRate 做入参校验,保证业务不变式;get dynamicPrice 实现销量驱动的定价策略。
5.4 批量属性定义
使用Object.defineProperties进行批量操作:
javascript
// 【代码注释】ORM 风格示例:用访问器 + defineProperties 封装脏检查,模拟「只 UPDATE 变更列」
class DatabaseModel {
constructor(tableName, data = {}) {
this.tableName = tableName;
this._data = data;
this._modifiedFields = new Set(); // 脏字段集合,O(1) 增删
this._isNew = true;
Object.defineProperties(this, {
tableName: {
value: tableName,
writable: false,
enumerable: true,
configurable: false
},
modifiedFields: {
get() {
return Array.from(this._modifiedFields); // 对外暴露快照数组
},
enumerable: true,
configurable: true
},
isNew: {
get() { return this._isNew; },
enumerable: true,
configurable: true
},
data: {
get() {
return { ...this._data }; // 防止外部直接 mutate 内部 _data
},
set(value) {
const oldData = { ...this._data };
this._data = { ...value };
Object.keys(value).forEach(key => {
if (oldData[key] !== value[key]) {
this._modifiedFields.add(key);
}
});
},
enumerable: true,
configurable: true
}
});
}
save() {
const modifiedData = {};
this._modifiedFields.forEach(field => {
modifiedData[field] = this._data[field];
});
console.log(`保存到表 ${this.tableName}:`, modifiedData);
this._modifiedFields.clear();
this._isNew = false;
}
}
const userModel = new DatabaseModel('users', {
id: 1,
username: 'zhangsan',
email: 'zhangsan@example.com'
});
console.log('初始状态:', userModel.isNew); // true
userModel.data = {
...userModel.data,
email: 'newemail@example.com'
};
console.log('修改的字段:', userModel.modifiedFields); // ['email']
userModel.save();
【代码注释】 Object.defineProperties 批量配置:一次调用设置多个属性的描述符,适合 ORM / 模型层场景;modifiedFields getter 自动追踪脏字段,save() 只提交变更列。
5.5 对象密封与冻结
对象保护级别的完整对比:
#mermaid-svg-lapWSWfKwZLjj87E{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-lapWSWfKwZLjj87E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lapWSWfKwZLjj87E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lapWSWfKwZLjj87E .error-icon{fill:#552222;}#mermaid-svg-lapWSWfKwZLjj87E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lapWSWfKwZLjj87E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lapWSWfKwZLjj87E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lapWSWfKwZLjj87E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lapWSWfKwZLjj87E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lapWSWfKwZLjj87E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lapWSWfKwZLjj87E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lapWSWfKwZLjj87E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lapWSWfKwZLjj87E .marker.cross{stroke:#333333;}#mermaid-svg-lapWSWfKwZLjj87E svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lapWSWfKwZLjj87E p{margin:0;}#mermaid-svg-lapWSWfKwZLjj87E .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lapWSWfKwZLjj87E .cluster-label text{fill:#333;}#mermaid-svg-lapWSWfKwZLjj87E .cluster-label span{color:#333;}#mermaid-svg-lapWSWfKwZLjj87E .cluster-label span p{background-color:transparent;}#mermaid-svg-lapWSWfKwZLjj87E .label text,#mermaid-svg-lapWSWfKwZLjj87E span{fill:#333;color:#333;}#mermaid-svg-lapWSWfKwZLjj87E .node rect,#mermaid-svg-lapWSWfKwZLjj87E .node circle,#mermaid-svg-lapWSWfKwZLjj87E .node ellipse,#mermaid-svg-lapWSWfKwZLjj87E .node polygon,#mermaid-svg-lapWSWfKwZLjj87E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lapWSWfKwZLjj87E .rough-node .label text,#mermaid-svg-lapWSWfKwZLjj87E .node .label text,#mermaid-svg-lapWSWfKwZLjj87E .image-shape .label,#mermaid-svg-lapWSWfKwZLjj87E .icon-shape .label{text-anchor:middle;}#mermaid-svg-lapWSWfKwZLjj87E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lapWSWfKwZLjj87E .rough-node .label,#mermaid-svg-lapWSWfKwZLjj87E .node .label,#mermaid-svg-lapWSWfKwZLjj87E .image-shape .label,#mermaid-svg-lapWSWfKwZLjj87E .icon-shape .label{text-align:center;}#mermaid-svg-lapWSWfKwZLjj87E .node.clickable{cursor:pointer;}#mermaid-svg-lapWSWfKwZLjj87E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lapWSWfKwZLjj87E .arrowheadPath{fill:#333333;}#mermaid-svg-lapWSWfKwZLjj87E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lapWSWfKwZLjj87E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lapWSWfKwZLjj87E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lapWSWfKwZLjj87E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lapWSWfKwZLjj87E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lapWSWfKwZLjj87E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lapWSWfKwZLjj87E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lapWSWfKwZLjj87E .cluster text{fill:#333;}#mermaid-svg-lapWSWfKwZLjj87E .cluster span{color:#333;}#mermaid-svg-lapWSWfKwZLjj87E 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-lapWSWfKwZLjj87E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lapWSWfKwZLjj87E rect.text{fill:none;stroke-width:0;}#mermaid-svg-lapWSWfKwZLjj87E .icon-shape,#mermaid-svg-lapWSWfKwZLjj87E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lapWSWfKwZLjj87E .icon-shape p,#mermaid-svg-lapWSWfKwZLjj87E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lapWSWfKwZLjj87E .icon-shape .label rect,#mermaid-svg-lapWSWfKwZLjj87E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lapWSWfKwZLjj87E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lapWSWfKwZLjj87E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lapWSWfKwZLjj87E :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 对象保护级别
普通对象
防止扩展
密封对象
冻结对象
可添加/删除/修改属性
不可添加新属性
可删除/修改现有属性
不可添加/删除属性
可修改现有属性值
不可添加/删除/修改属性
完全只读
【代码注释】 对象保护级别对比:普通对象 < preventExtensions(禁添)< seal(禁添禁删)< freeze(完全只读)。配置项用 freeze,运行时状态用 seal。
对象保护实战应用:
javascript
// 【代码注释】freeze vs seal:静态配置完全只读;运行时上下文允许改值但禁止增删键
class ConfigurationManager {
constructor() {
const defaultConfig = {
apiVersion: 'v2',
timeout: 30000,
retryAttempts: 3,
debugMode: false,
logLevel: 'info'
};
const productionConfig = {
timeout: 60000,
debugMode: false,
logLevel: 'error'
};
const developmentConfig = {
timeout: 10000,
debugMode: true,
logLevel: 'debug'
};
this.config = Object.freeze({
...defaultConfig,
...(this.detectEnvironment() === 'production' ? productionConfig : developmentConfig)
});
this.runtimeConfig = Object.seal({
currentUserId: null,
sessionId: null,
lastActivity: Date.now()
});
}
detectEnvironment() {
return process.env.NODE_ENV || 'development';
}
getConfig() {
return this.config; // 返回冻结对象引用,调用方不应尝试改写
}
updateRuntimeConfig(updates) {
Object.keys(updates).forEach(key => {
if (key in this.runtimeConfig) {
this.runtimeConfig[key] = updates[key];
}
// seal 下新增键如 updates.foo 会被忽略(非严格)或抛错(严格)
});
}
checkObjectStates() {
return {
config: {
frozen: Object.isFrozen(this.config),
sealed: Object.isSealed(this.config),
extensible: Object.isExtensible(this.config)
},
runtimeConfig: {
frozen: Object.isFrozen(this.runtimeConfig),
sealed: Object.isSealed(this.runtimeConfig),
extensible: Object.isExtensible(this.runtimeConfig)
}
};
}
}
// 【代码注释】使用配置管理器
const configManager = new ConfigurationManager();
// 尝试修改冻结的配置(严格模式下会报错)
try {
configManager.config.timeout = 99999; // 静默失败
} catch (error) {
console.error('无法修改冻结的配置');
}
// 验证配置未改变
console.log('配置超时值:', configManager.config.timeout); // 仍为原值
// 更新运行时配置(密封对象允许修改现有属性)
configManager.updateRuntimeConfig({
currentUserId: 12345,
sessionId: 'session_abc123'
});
console.log('运行时配置:', configManager.runtimeConfig);
console.log('对象状态检查:', configManager.checkObjectStates());
【代码注释】 生产级配置管理:Object.freeze 确保静态配置不可变;Object.seal 允许修改现有键值(如 sessionId)但阻止新增键;isFrozen/isSealed 用于运行时断言。
6. 深拷贝与浅拷贝深度解析
6.1 拷贝技术原理对比
名词解析:深拷贝(Deep Copy)
深拷贝会递归复制对象的所有层级,创建完全独立的副本,原始对象和拷贝对象之间不共享任何引用。
#mermaid-svg-YSjEUbcM8QWRHWce{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-YSjEUbcM8QWRHWce .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YSjEUbcM8QWRHWce .error-icon{fill:#552222;}#mermaid-svg-YSjEUbcM8QWRHWce .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YSjEUbcM8QWRHWce .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YSjEUbcM8QWRHWce .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YSjEUbcM8QWRHWce .marker.cross{stroke:#333333;}#mermaid-svg-YSjEUbcM8QWRHWce svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YSjEUbcM8QWRHWce p{margin:0;}#mermaid-svg-YSjEUbcM8QWRHWce .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YSjEUbcM8QWRHWce .cluster-label text{fill:#333;}#mermaid-svg-YSjEUbcM8QWRHWce .cluster-label span{color:#333;}#mermaid-svg-YSjEUbcM8QWRHWce .cluster-label span p{background-color:transparent;}#mermaid-svg-YSjEUbcM8QWRHWce .label text,#mermaid-svg-YSjEUbcM8QWRHWce span{fill:#333;color:#333;}#mermaid-svg-YSjEUbcM8QWRHWce .node rect,#mermaid-svg-YSjEUbcM8QWRHWce .node circle,#mermaid-svg-YSjEUbcM8QWRHWce .node ellipse,#mermaid-svg-YSjEUbcM8QWRHWce .node polygon,#mermaid-svg-YSjEUbcM8QWRHWce .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YSjEUbcM8QWRHWce .rough-node .label text,#mermaid-svg-YSjEUbcM8QWRHWce .node .label text,#mermaid-svg-YSjEUbcM8QWRHWce .image-shape .label,#mermaid-svg-YSjEUbcM8QWRHWce .icon-shape .label{text-anchor:middle;}#mermaid-svg-YSjEUbcM8QWRHWce .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YSjEUbcM8QWRHWce .rough-node .label,#mermaid-svg-YSjEUbcM8QWRHWce .node .label,#mermaid-svg-YSjEUbcM8QWRHWce .image-shape .label,#mermaid-svg-YSjEUbcM8QWRHWce .icon-shape .label{text-align:center;}#mermaid-svg-YSjEUbcM8QWRHWce .node.clickable{cursor:pointer;}#mermaid-svg-YSjEUbcM8QWRHWce .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YSjEUbcM8QWRHWce .arrowheadPath{fill:#333333;}#mermaid-svg-YSjEUbcM8QWRHWce .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YSjEUbcM8QWRHWce .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YSjEUbcM8QWRHWce .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YSjEUbcM8QWRHWce .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YSjEUbcM8QWRHWce .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YSjEUbcM8QWRHWce .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YSjEUbcM8QWRHWce .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YSjEUbcM8QWRHWce .cluster text{fill:#333;}#mermaid-svg-YSjEUbcM8QWRHWce .cluster span{color:#333;}#mermaid-svg-YSjEUbcM8QWRHWce 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-YSjEUbcM8QWRHWce .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YSjEUbcM8QWRHWce rect.text{fill:none;stroke-width:0;}#mermaid-svg-YSjEUbcM8QWRHWce .icon-shape,#mermaid-svg-YSjEUbcM8QWRHWce .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YSjEUbcM8QWRHWce .icon-shape p,#mermaid-svg-YSjEUbcM8QWRHWce .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YSjEUbcM8QWRHWce .icon-shape .label rect,#mermaid-svg-YSjEUbcM8QWRHWce .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YSjEUbcM8QWRHWce .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YSjEUbcM8QWRHWce .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YSjEUbcM8QWRHWce :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始对象
浅拷贝
深拷贝
共享嵌套对象引用
完全独立的副本
修改嵌套属性
影响原对象
修改任何属性
不影响原对象
【代码注释】 拷贝策略选型:[...arr] / {...obj} 适合顶层扁平数据;深拷贝处理嵌套与循环引用;structuredClone 原生方案性能最优,2022 年起所有现代浏览器支持。
6.2 深拷贝完整实现
生产级深拷贝函数:
javascript
// 【代码注释】手写深拷贝要点:类型分支 + 循环引用表 + 原型链 + Symbol 键
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
function deepClone(obj, hash = new WeakMap()) {
// 原始类型与 null:按值返回;函数通常不克隆(structuredClone 亦不支持)
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags);
}
if (obj instanceof Map) {
const clone = new Map();
obj.forEach((value, key) => {
clone.set(deepClone(key, hash), deepClone(value, hash));
});
return clone;
}
if (obj instanceof Set) {
const clone = new Set();
obj.forEach(value => {
clone.add(deepClone(value, hash));
});
return clone;
}
if (obj instanceof ArrayBuffer) {
return obj.slice(0);
}
// WeakMap:键为对象时不阻止 GC;已访问过的对象直接返回已创建的克隆,打破环
if (hash.has(obj)) {
return hash.get(obj);
}
const Ctor = obj.constructor;
if (Ctor !== Object && Ctor !== Array) {
try {
return new Ctor(obj);
} catch (error) {
console.warn('无法克隆特殊对象类型:', Ctor.name);
return obj;
}
}
const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
hash.set(obj, clone); // 必须在递归子属性之前登记,否则循环引用会栈溢出
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (const symKey of symbolKeys) {
clone[symKey] = deepClone(obj[symKey], hash);
}
return clone;
}
const complexObject = {
primitive: 123,
string: 'hello',
array: [1, 2, 3, { nested: 'value' }],
date: new Date(),
regex: /test/g,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
nested: { level1: { level2: { value: 'deeply nested' } } },
func: function() { return 'original'; }
};
complexObject.circular = complexObject;
const clonedObject = deepClone(complexObject);
console.log('原始对象循环引用:', complexObject.circular === complexObject);
console.log('拷贝对象循环引用:', clonedObject.circular === clonedObject);
console.log('根对象独立:', complexObject !== clonedObject);
console.log('嵌套对象独立:', complexObject.nested !== clonedObject.nested);
【代码注释】 生产级深拷贝:WeakMap 记录已访问对象防循环引用;分支处理 Date/RegExp/Map/Set/ArrayBuffer;递归拷贝 for...in 自有属性与 Symbol 键;structuredClone 不能处理函数。
6.3 现代深拷贝方案对比
2024年推荐深拷贝方案:
javascript
// 【代码注释】现代深拷贝选型:优先 structuredClone,其次 JSON,最后手写/lodash
function modernDeepClone(obj) {
try {
return structuredClone(obj);
// 支持:Date/RegExp/Map/Set/ArrayBuffer/循环引用
// 不支持:函数/DOM 节点/部分内置对象 → 抛 DOMException
} catch (error) {
console.warn('structuredClone失败,使用备用方案:', error);
return fallbackDeepClone(obj);
}
}
function jsonDeepClone(obj) {
try {
return JSON.parse(JSON.stringify(obj));
// 丢失:undefined/Symbol/函数;Date 变字符串;不支持循环引用
} catch (error) {
console.error('JSON深拷贝失败:', error);
return null;
}
}
// 【代码注释】方案三:Lodash cloneDeep(需要引入库)
// import _ from 'lodash';
// const lodashClone = (obj) => _.cloneDeep(obj);
// 【代码注释】性能测试函数
function benchmarkDeepClones() {
const testObject = {
user: {
id: 1,
name: '张三',
profile: {
age: 25,
address: {
city: '北京',
district: '朝阳区'
}
}
},
orders: [
{ id: 'A001', items: ['商品1', '商品2'] },
{ id: 'A002', items: ['商品3', '商品4'] }
]
};
const methods = [
{
name: 'structuredClone',
fn: () => structuredClone(testObject)
},
{
name: 'JSON方法',
fn: () => JSON.parse(JSON.stringify(testObject))
},
{
name: '自定义deepClone',
fn: () => deepClone(testObject)
}
];
const results = {};
methods.forEach(method => {
const start = performance.now();
const iterations = 10000;
for (let i = 0; i < iterations; i++) {
method.fn();
}
const end = performance.now();
const avgTime = (end - start) / iterations;
results[method.name] = {
平均耗时: `${avgTime.toFixed(4)}ms`,
性能评分: avgTime < 0.01 ? '优秀' : avgTime < 0.1 ? '良好' : '一般'
};
});
return results;
}
// 【代码注释】运行性能测试
console.log('深拷贝性能测试:', benchmarkDeepClones());
【代码注释】 三种方案对比:structuredClone(原生,推荐)支持 Date/Map/Set/循环引用,但不支持函数;JSON 方法丢失 undefined/函数/Symbol;手写方案可定制但维护成本高。
6.4 实战应用:状态管理系统
javascript
// 【代码注释】迷你 Redux:内部单源状态 + 订阅发布 + 深拷贝隔离 + 历史栈 undo
class StateManager {
constructor(initialState = {}) {
this._state = deepClone(initialState);
this._listeners = [];
this._history = [];
this._maxHistorySize = 50;
}
getState() {
return deepClone(this._state); // 外部拿到的副本无法污染内部 _state
}
setState(updater) {
const oldState = this._state;
let newState;
if (typeof updater === 'function') {
newState = updater(deepClone(oldState)); // 函数式更新,类似 React setState
} else {
newState = deepClone(updater);
}
this._history.push(deepClone(oldState));
if (this._history.length > this._maxHistorySize) {
this._history.shift(); // 限制栈深,防止内存无限增长
}
this._state = newState;
this.notifyListeners();
}
undo() {
if (this._history.length > 0) {
this._state = this._history.pop();
this.notifyListeners();
}
}
subscribe(listener) {
this._listeners.push(listener);
return () => {
const index = this._listeners.indexOf(listener);
if (index > -1) {
this._listeners.splice(index, 1);
}
};
}
notifyListeners() {
const stateCopy = this.getState();
this._listeners.forEach(listener => listener(stateCopy));
}
}
const cartState = new StateManager({
items: [],
total: 0,
discount: 0
});
cartState.subscribe((state) => {
console.log('购物车状态更新:', state);
});
cartState.setState((prevState) => ({
...prevState,
items: [...prevState.items, { id: 1, name: '商品1', price: 100, quantity: 1 }]
}));
cartState.setState((prevState) => ({
...prevState,
items: prevState.items.map(item =>
item.id === 1 ? { ...item, quantity: item.quantity + 1 } : item
)
}));
【代码注释】 简版状态管理器:deepClone 确保取出的状态不被外部修改;setState 先记录旧状态到历史栈再更新;undo 弹出栈顶回滚;subscribe 返回取消订阅函数,防内存泄漏。
7. 实战应用场景
7.1 电商平台数据处理
javascript
// 【代码注释】Pipeline:每个 processor 是纯函数 data => data,reduce 从左到右串联
class ECommerceProcessor {
constructor() {
this.processors = [];
}
use(processor) {
this.processors.push(processor);
return this; // 链式调用 .use().use()
}
process(data) {
return this.processors.reduce((result, processor) => {
return processor(result);
}, data);
}
}
const normalizeData = (data) => {
return data.map(item => ({
id: item.product_id || item.id,
name: item.product_name || item.name,
price: parseFloat(item.price) || 0,
category: item.category || '未分类',
stock: parseInt(item.stock) || 0,
images: Array.isArray(item.images) ? item.images : []
}));
};
// 【代码注释】价格计算处理器
const calculatePrices = (data) => {
return data.map(item => {
const discount = item.discount || 0;
return {
...item,
originalPrice: item.price,
finalPrice: item.price * (1 - discount / 100),
savings: item.price * (discount / 100)
};
});
};
// 【代码注释】库存检查处理器
const checkStock = (data) => {
return data.filter(item => item.stock > 0).map(item => ({
...item,
inStock: true,
stockStatus: item.stock > 10 ? '充足' : '紧张'
}));
};
// 【代码注释】分类整理处理器
const categorizeProducts = (data) => {
const categories = {};
data.forEach(item => {
if (!categories[item.category]) {
categories[item.category] = [];
}
categories[item.category].push(item);
});
return {
allProducts: data,
categories,
categoryCount: Object.keys(categories).length,
totalProducts: data.length
};
};
// 【代码注释】使用处理管道
const rawData = [
{ product_id: '1', product_name: '手机', price: '2999', category: '电子产品', stock: '50', discount: 10 },
{ product_id: '2', product_name: '笔记本', price: '5999', category: '电子产品', stock: '5', discount: 15 },
{ id: '3', name: 'T恤', price: '99', category: '服装', stock: '0', discount: 0 }
];
const processor = new ECommerceProcessor()
.use(normalizeData)
.use(calculatePrices)
.use(checkStock)
.use(categorizeProducts);
const processedData = processor.process(rawData);
console.log('处理后的电商数据:', processedData);
【代码注释】 数据处理管道(Pipeline 模式):每个处理器只负责单一转换(标准化→价格计算→库存过滤→分类),链式 use() 注册、reduce 驱动,符合开闭原则,易于单测。
7.2 用户权限管理系统
javascript
// 【代码注释】RBAC:角色 → 权限列表;用户 → 权限 Set;鉴权 O(1) 的 has 查询
class PermissionManager {
constructor() {
this.roles = new Map([
['admin', ['create', 'read', 'update', 'delete', 'manage_users']],
['editor', ['create', 'read', 'update']],
['viewer', ['read']]
]);
this.userPermissions = new Map(); // userId -> Set<permission>
}
assignRole(userId, role) {
if (!this.roles.has(role)) {
throw new Error(`未知角色:${role}`);
}
const permissions = this.roles.get(role);
this.userPermissions.set(userId, new Set(permissions));
return this;
}
hasPermission(userId, permission) {
const userPermissions = this.userPermissions.get(userId);
return userPermissions ? userPermissions.has(permission) : false;
}
addPermission(userId, permission) {
if (!this.userPermissions.has(userId)) {
this.userPermissions.set(userId, new Set());
}
this.userPermissions.get(userId).add(permission);
return this;
}
removePermission(userId, permission) {
const permissions = this.userPermissions.get(userId);
if (permissions) {
permissions.delete(permission);
}
return this;
}
getUserPermissions(userId) {
const permissions = this.userPermissions.get(userId);
return permissions ? Array.from(permissions) : [];
}
}
const authManager = new PermissionManager();
// 分配角色
authManager.assignRole('user123', 'editor');
authManager.addPermission('user123', 'publish'); // 额外授予发布权限
// 检查权限
console.log('user123可以编辑:', authManager.hasPermission('user123', 'update')); // true
console.log('user123可以删除:', authManager.hasPermission('user123', 'delete')); // false
console.log('user123可以发布:', authManager.hasPermission('user123', 'publish')); // true
// 获取所有权限
console.log('user123的权限列表:', authManager.getUserPermissions('user123'));
【代码注释】 RBAC 权限管理:Map 存储角色→权限映射;Set 存储用户权限集合,has O(1) 查找;assignRole + addPermission 支持细粒度自定义;Array.from 将 Set 转数组供序列化。
8. 性能优化建议
8.1 内存管理最佳实践
javascript
// 【代码注释】WeakMap 做对象元数据:不增加强引用计数,对象被 GC 后条目自动消失
class MemoryMonitor {
constructor() {
this.trackedObjects = new WeakMap();
this.memoryUsage = [];
}
track(object, label) {
this.trackedObjects.set(object, {
label,
created: Date.now(),
size: this.estimateSize(object)
});
}
estimateSize(obj) {
let size = 0;
if (typeof obj === 'string') {
size = obj.length * 2; // UTF-16编码
} else if (typeof obj === 'number') {
size = 8;
} else if (typeof obj === 'boolean') {
size = 4;
} else if (obj instanceof Array) {
size = obj.length * 8; // 粗略估算
obj.forEach(item => {
size += this.estimateSize(item);
});
} else if (typeof obj === 'object' && obj !== null) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
size += key.length * 2;
size += this.estimateSize(obj[key]);
}
}
}
return size;
}
// 【代码注释】生成内存报告
generateReport() {
const report = {
timestamp: Date.now(),
trackedObjects: this.trackedObjects.size,
totalSize: 0,
objects: []
};
// 注意:WeakMap不可遍历,这里只是示例结构
console.log('内存报告生成中...');
return report;
}
}
// 【代码注释】使用内存监控
const memoryMonitor = new MemoryMonitor();
const largeDataSet = {
users: Array(1000).fill(0).map((_, i) => ({
id: i,
name: `用户${i}`,
data: new Array(100).fill('测试数据')
}))
};
memoryMonitor.track(largeDataSet, '大数据集');
【代码注释】 WeakMap 追踪对象而不阻止 GC:当被追踪对象被回收时,条目自动消失,无需手动清理;estimateSize 按类型递归估算内存占用,适合开发阶段性能分析工具。
8.2 数据处理优化技巧
javascript
// 【代码注释】大批量任务:分片处理 + 宏任务让出主线程,避免长任务阻塞渲染(>50ms)
class BatchProcessor {
constructor(batchSize = 100) {
this.batchSize = batchSize;
this.queue = [];
this.processing = false;
}
add(item) {
this.queue.push(item);
if (!this.processing) {
this.processQueue();
}
}
async processQueue() {
this.processing = true;
while (this.queue.length > 0) {
const batch = this.queue.splice(0, this.batchSize);
await this.processBatch(batch);
await new Promise(resolve => setTimeout(resolve, 0));
}
this.processing = false;
}
async processBatch(batch) {
console.log(`处理批次:${batch.length}项`);
// 模拟异步操作
return new Promise(resolve => {
setTimeout(() => {
resolve(batch.map(item => ({ ...item, processed: true })));
}, 100);
});
}
}
// 【代码注释】使用批量处理器
const processor = new BatchProcessor(50);
// 添加大量数据
for (let i = 0; i < 1000; i++) {
processor.add({ id: i, data: `项目${i}` });
}
【代码注释】 批量处理器的关键技巧:setTimeout(resolve, 0) 让出主线程,防止大批量任务阻塞 UI 渲染;splice(0, batchSize) 每次取固定量处理,控制内存峰值。
9. 最佳实践总结
9.1 代码设计原则
SOLID原则在JavaScript中的应用:
javascript
// 【代码注释】SOLID 在 JS 中的落地:拆分类职责、策略对象扩展、构造函数注入依赖
class UserRepository {
constructor(db) {
this.db = db;
}
async findById(id) {
return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
}
async create(userData) {
return this.db.query('INSERT INTO users SET ?', userData);
}
}
class EmailService {
constructor(smtpConfig) {
this.smtp = smtpConfig;
}
async sendWelcomeEmail(userEmail, userName) {
// 发送欢迎邮件逻辑
}
}
// 【代码注释】开闭原则(OCP)
class PaymentProcessor {
async processPayment(paymentMethod, amount) {
const processor = this.getProcessor(paymentMethod);
return processor.process(amount);
}
getProcessor(method) {
const processors = {
'credit_card': new CreditCardProcessor(),
'paypal': new PayPalProcessor(),
'wechat': new WeChatProcessor()
};
return processors[method] || new DefaultProcessor();
}
}
// 【代码注释】依赖倒置原则(DIP)
class OrderService {
constructor(paymentProcessor, notificationService) {
this.paymentProcessor = paymentProcessor;
this.notificationService = notificationService;
}
async createOrder(orderData) {
// 处理订单逻辑
await this.paymentProcessor.processPayment(orderData.payment);
await this.notificationService.sendConfirmation(orderData.userEmail);
}
}
【代码注释】 SOLID 三原则示范:UserRepository / EmailService 各司其职(SRP);PaymentProcessor.getProcessor 对扩展开放、对修改封闭(OCP);OrderService 依赖抽象接口(DIP)。
9.2 错误处理模式
javascript
// 【代码注释】Result(铁路导向):用返回值承载失败,避免 try/catch 打断业务流
class Result {
constructor(success, data, error) {
this.success = success;
this.data = data;
this.error = error;
}
static ok(data) {
return new Result(true, data, null);
}
static error(error) {
return new Result(false, null, error);
}
map(fn) {
return this.success ?
Result.ok(fn(this.data)) :
Result.error(this.error);
}
flatMap(fn) {
return this.success ?
fn(this.data) : // fn 须返回 Result,用于可能失败的下一步
Result.error(this.error);
}
unwrap() {
if (this.success) {
return this.data;
} else {
throw new Error(`Operation failed: ${this.error}`);
}
}
}
function validateUser(userData) {
if (!userData.email || !userData.email.includes('@')) {
return Result.error('无效的邮箱地址');
}
if (!userData.password || userData.password.length < 6) {
return Result.error('密码长度不能少于6位');
}
return Result.ok(userData);
}
function createUser(userData) {
return validateUser(userData)
.flatMap(validated => saveUserToDatabase(validated))
.map(user => sendWelcomeEmail(user.email));
}
// 【代码注释】调用示例
const result = createUser({
email: 'user@example.com',
password: 'secure123'
});
if (result.success) {
console.log('用户创建成功:', result.data);
} else {
console.error('用户创建失败:', result.error);
}
【代码注释】 Result 模式(Railway-Oriented Programming):用 Result.ok/Result.error 代替抛异常,链式 map/flatMap 传递成功值,失败时短路,调用方显式处理两条路径。
9.3 异步编程最佳实践
javascript
// 【代码注释】Promise 工具集:容错聚合、限流批处理、线性退避重试
class PromiseUtils {
static async allSafe(promises) {
const results = await Promise.allSettled(promises);
const successes = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failures = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
return {
successes,
failures,
allSuccessful: failures.length === 0
};
}
static async batch(items, processor, batchSize = 10) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(item => processor(item))
);
results.push(...batchResults);
}
return results;
}
static async retry(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`重试第${i + 1}次...`);
await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
}
}
}
}
async function processMultipleAPIs() {
const apiCalls = [
fetch('https://api.example.com/users'),
fetch('https://api.example.com/products'),
fetch('https://api.example.com/orders')
];
const result = await PromiseUtils.allSafe(apiCalls);
if (result.allSuccessful) {
console.log('所有API调用成功');
return result.successes;
} else {
console.warn('部分API调用失败:', result.failures);
return result.successes;
}
}
【代码注释】 PromiseUtils 三把利器:allSafe 用 allSettled 容错(不因单个失败中断);batch 控制并发数防止请求风暴;retry 指数退避重试,生产级 API 客户端必备。
10. Day04 知识点速查
| 模块 | 核心要点 | 框架关联 |
|---|---|---|
| 数据类型 | 7 原始 + 多对象类型;原始值传递、引用共享 | 所有框架基础 |
| 变量声明 | let/const 不提升、不重复声明、块级作用域 TDZ |
ESLint prefer-const |
| 箭头函数 | 词法 this;不可 new;无 arguments |
React/Vue 回调、Promise 链 |
| 模板字符串 | 多行、插值、标签模板(Tagged Template) | styled-components、gql |
| 解构/展开 | 按位置/名称提取;...rest 收集;对象合并 |
Redux 状态更新、React props |
| ES6 模块 | 静态分析、Tree Shaking、Live Binding | Vite/Webpack 打包优化 |
| Class 类 | 私有字段 #、静态、继承、Mixin |
React 类组件、Vue Options API |
| Iterator/Generator | 惰性求值、自定义迭代、状态机、异步流 | 分页游标、for await...of |
| Proxy/Reflect | 透明拦截、元编程、响应式系统 | Vue 3 reactive、数据验证 |
| 可选链/空值合并 | ?. 安全访问、?? 区分合法假值 |
API 响应解析、默认参数 |
| 数组 | flat/flatMap 扁平化;slice/[...] 浅拷贝 |
数据管道处理 |
| 对象描述符 | defineProperty;get/set 访问器 |
Vue 2 响应式 |
| 深拷贝 | structuredClone > JSON > 手写;WeakMap 防环 |
状态管理、Undo/Redo |
#mermaid-svg-ic2Onww8ym08ZbvV{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-ic2Onww8ym08ZbvV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ic2Onww8ym08ZbvV .error-icon{fill:#552222;}#mermaid-svg-ic2Onww8ym08ZbvV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ic2Onww8ym08ZbvV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ic2Onww8ym08ZbvV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ic2Onww8ym08ZbvV .marker.cross{stroke:#333333;}#mermaid-svg-ic2Onww8ym08ZbvV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ic2Onww8ym08ZbvV p{margin:0;}#mermaid-svg-ic2Onww8ym08ZbvV .edge{stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .section--1 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section--1 path,#mermaid-svg-ic2Onww8ym08ZbvV .section--1 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section--1 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section--1 text{fill:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth--1{stroke-width:17;}#mermaid-svg-ic2Onww8ym08ZbvV .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-0 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-0 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-0 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-0 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-0 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-0{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-0{stroke-width:14;}#mermaid-svg-ic2Onww8ym08ZbvV .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-1 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-1 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-1 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-1 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-1 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-1{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-1{stroke-width:11;}#mermaid-svg-ic2Onww8ym08ZbvV .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-2 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-2 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-2 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-2 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-2 text{fill:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-2{stroke-width:8;}#mermaid-svg-ic2Onww8ym08ZbvV .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-3 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-3 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-3 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-3 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-3 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-3{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-3{stroke-width:5;}#mermaid-svg-ic2Onww8ym08ZbvV .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-4 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-4 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-4 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-4 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-4 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-4{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-4{stroke-width:2;}#mermaid-svg-ic2Onww8ym08ZbvV .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-5 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-5 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-5 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-5 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-5 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-5{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-5{stroke-width:-1;}#mermaid-svg-ic2Onww8ym08ZbvV .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-6 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-6 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-6 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-6 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-6 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-6{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-6{stroke-width:-4;}#mermaid-svg-ic2Onww8ym08ZbvV .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-7 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-7 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-7 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-7 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-7 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-7{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-7{stroke-width:-7;}#mermaid-svg-ic2Onww8ym08ZbvV .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-8 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-8 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-8 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-8 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-8 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-8{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-8{stroke-width:-10;}#mermaid-svg-ic2Onww8ym08ZbvV .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-9 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-9 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-9 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-9 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-9 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-9{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-9{stroke-width:-13;}#mermaid-svg-ic2Onww8ym08ZbvV .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-10 rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-10 path,#mermaid-svg-ic2Onww8ym08ZbvV .section-10 circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-10 polygon,#mermaid-svg-ic2Onww8ym08ZbvV .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-10 text{fill:black;}#mermaid-svg-ic2Onww8ym08ZbvV .node-icon-10{font-size:40px;color:black;}#mermaid-svg-ic2Onww8ym08ZbvV .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .edge-depth-10{stroke-width:-16;}#mermaid-svg-ic2Onww8ym08ZbvV .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled,#mermaid-svg-ic2Onww8ym08ZbvV .disabled circle,#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:lightgray;}#mermaid-svg-ic2Onww8ym08ZbvV .disabled text{fill:#efefef;}#mermaid-svg-ic2Onww8ym08ZbvV .section-root rect,#mermaid-svg-ic2Onww8ym08ZbvV .section-root path,#mermaid-svg-ic2Onww8ym08ZbvV .section-root circle,#mermaid-svg-ic2Onww8ym08ZbvV .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-ic2Onww8ym08ZbvV .section-root text{fill:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .section-root span{color:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .section-2 span{color:#ffffff;}#mermaid-svg-ic2Onww8ym08ZbvV .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-ic2Onww8ym08ZbvV .edge{fill:none;}#mermaid-svg-ic2Onww8ym08ZbvV .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-ic2Onww8ym08ZbvV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ES6 完全指南总结
类型系统
原始 vs 引用
BigInt Symbol
声明与作用域
let const TDZ
块级作用域
函数增强
箭头函数 词法this
模板字符串
默认参数 Rest
数据操作
解构赋值
展开运算符
可选链 ??
面向对象
Class 私有字段
继承 super
Mixin 模式
迭代与生成
Iterator 协议
Generator 惰性
for...of async
元编程
Proxy Reflect
响应式系统
模块化
ESM import export
动态 import
Tree Shaking
数据处理
flat flatMap
浅拷贝 深拷贝
structuredClone
【代码注释】 ES6 完整总结思维导图:从类型体系出发,经过声明/函数/类/迭代/元编程/模块,最终落地数据处理。每个节点都有与之对应的主流框架(Vue/React/Node.js)的实际应用------这张地图可作为面试复盘和技术选型的速查参考。
总结
本指南涵盖了ES6从基础到高级的完整知识体系与实战应用技巧:
核心知识点回顾:
- 数据类型系统:7种原始类型和多种对象类型的深入理解,BigInt/Symbol 使用场景
- 变量声明:let/const vs var,块级作用域、TDZ 的重要性,ESLint 最佳实践
- 函数增强:箭头函数词法 this、模板字符串与标签模板、默认参数、Rest/Spread
- 解构与展开:数组/对象解构、不可变更新模式、函数参数自文档化
- ES6 模块:ESM 静态分析、Tree Shaking、Live Binding 与 CJS 的本质差异
- Class 类系统 :私有字段(
#)、静态成员、继承链、Mixin 多继承模拟 - Iterator / Generator:可迭代协议、惰性求值、分页游标、状态机、异步迭代
- Proxy / Reflect:透明拦截、响应式系统原理(Vue 3 核心)、数据验证、AOP
- 可选链与空值合并 :
?.安全访问、??区分合法假值、逻辑赋值运算符 - 数组操作:扁平化、拷贝技术的多种实现和性能对比
- 对象特性:属性描述符、访问器属性的实战应用(Vue 2 响应式基础)
- 深拷贝技术:从基础到生产级的完整解决方案,structuredClone 现代推荐
- 实战应用:电商数据管道、RBAC 权限管理、状态管理系统等真实业务场景
学习路径建议:
- 初学者:重点掌握数据类型、let/const 块级作用域、箭头函数、模板字符串、基础解构
- 中级开发者:深入理解 Class 继承、Iterator/Generator、对象描述符、浅/深拷贝、异步编程
- 高级开发者:关注 Proxy/Reflect 元编程、ESM Tree Shaking、性能优化、架构设计(SOLID/Pipeline/RBAC)
- 框架学习者:用 Proxy 理解 Vue 3 响应式;用展开运算符理解 Redux 不可变更新;用 Generator 理解 async/await 原理
持续学习资源:
- ECMAScript官方规范:ECMA-262
- MDN Web Docs:JavaScript Guide
- 现代JavaScript教程:javascript.info
参考资料:
- MDN - Object.getOwnPropertyDescriptor()
- javascript.info - Property Descriptors
- MDN - Deep Copy
- Six Types of Scope in JavaScript
- Function Scope & Block Scope in JS
附录:官方与延伸阅读
| 资源 | 链接 | 说明 |
|---|---|---|
| MDN · 数据类型 | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures | 原始类型与对象 |
| MDN · defineProperty | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty | 属性描述符 |
| MDN · 深拷贝 | https://developer.mozilla.org/zh-CN/docs/Glossary/Deep_copy | 拷贝概念 |
| javascript.info | https://zh.javascript.info/object-properties | 对象属性详解 |
文中 HTML 示例可直接保存后在浏览器打开验证;对象/数组练习建议在控制台逐步执行。