ES6+ 核心特性深度解析:现代 JavaScript 开发的基石
一篇面向实战的 ES6 Day02 技术博客:涵盖数值、函数、数组、对象四大模块,配套完整可运行示例与知识点归纳。理论依据参考 MDN JavaScript 指南、ECMA-262 规范 及业界主流框架实践。
目录
- [0. 知识脉络与前置回顾](#0. 知识脉络与前置回顾)
- [0.1 本章知识图谱](#0.1 本章知识图谱)
- [0.2 Day01 核心回顾(承上启下)](#0.2 Day01 核心回顾(承上启下))
- [0.3 核心名词百科](#0.3 核心名词百科)
- [1. 引言:ES6+ 的现代化意义](#1. 引言:ES6+ 的现代化意义)
- [2. 数值系统的新增特性](#2. 数值系统的新增特性)
- [2.1 二进制与八进制表示法](#2.1 二进制与八进制表示法)
- [2.2 Number 构造函数的扩展](#2.2 Number 构造函数的扩展)
- [2.3 Math 对象的增强](#2.3 Math 对象的增强)
- [2.4 BigInt:处理大整数](#2.4 BigInt:处理大整数)
- [2.5 数字分隔符](#2.5 数字分隔符)
- [2.6 数值模块知识点归纳](#2.6 数值模块知识点归纳)
- [3. 函数增强特性](#3. 函数增强特性)
- [3.1 参数默认值](#3.1 参数默认值)
- [3.2 Rest 参数](#3.2 Rest 参数)
- [3.3 箭头函数](#3.3 箭头函数)
- [3.4 函数参数尾逗号](#3.4 函数参数尾逗号)
- [3.5 标签模板字符串](#3.5 标签模板字符串)
- [3.6 函数模块知识点归纳](#3.6 函数模块知识点归纳)
- [4. 数组操作革新](#4. 数组操作革新)
- [4.1 扩展运算符](#4.1 扩展运算符)
- [4.2 Array 构造函数的增强](#4.2 Array 构造函数的增强)
- [4.3 数组实例方法的新增特性](#4.3 数组实例方法的新增特性)
- [4.4 数组操作的实际应用](#4.4 数组操作的实际应用)
- [4.5 数组模块知识点归纳](#4.5 数组模块知识点归纳)
- [5. 对象系统现代化](#5. 对象系统现代化)
- [5.1 对象字面量语法增强](#5.1 对象字面量语法增强)
- [5.2 super 关键字](#5.2 super 关键字)
- [5.3 对象的扩展运算符](#5.3 对象的扩展运算符)
- [5.4 Object 构造函数的新方法](#5.4 Object 构造函数的新方法)
- [5.5 对象模块知识点归纳](#5.5 对象模块知识点归纳)
- [6. 最佳实践与性能优化](#6. 最佳实践与性能优化)
- [7. 总结与展望](#7. 总结与展望)
- [附录 A:特性与业界应用对照表](#附录 A:特性与业界应用对照表)
- [附录 B:官方与延伸阅读](#附录 B:官方与延伸阅读)
0. 知识脉络与前置回顾
0.1 本章知识图谱
本章在 Day01(let/const、解构赋值、模板字符串)的基础上,继续深入 数值 → 函数 → 数组 → 对象 四条主线,构成现代 JavaScript 日常开发的语法底座。
#mermaid-svg-UJLdSKOFN8IOoz2q{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-UJLdSKOFN8IOoz2q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UJLdSKOFN8IOoz2q .error-icon{fill:#552222;}#mermaid-svg-UJLdSKOFN8IOoz2q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UJLdSKOFN8IOoz2q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UJLdSKOFN8IOoz2q .marker.cross{stroke:#333333;}#mermaid-svg-UJLdSKOFN8IOoz2q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UJLdSKOFN8IOoz2q p{margin:0;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge{stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 text{fill:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth--1{stroke-width:17;}#mermaid-svg-UJLdSKOFN8IOoz2q .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-0{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-0{stroke-width:14;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-1{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-1{stroke-width:11;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 text{fill:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-2{stroke-width:8;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-3{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-3{stroke-width:5;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-4{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-4{stroke-width:2;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-5{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-5{stroke-width:-1;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-6{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-6{stroke-width:-4;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-7{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-7{stroke-width:-7;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-8{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-8{stroke-width:-10;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-9{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-9{stroke-width:-13;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 polygon,#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 text{fill:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .node-icon-10{font-size:40px;color:black;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .edge-depth-10{stroke-width:-16;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled circle,#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:lightgray;}#mermaid-svg-UJLdSKOFN8IOoz2q .disabled text{fill:#efefef;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-root rect,#mermaid-svg-UJLdSKOFN8IOoz2q .section-root path,#mermaid-svg-UJLdSKOFN8IOoz2q .section-root circle,#mermaid-svg-UJLdSKOFN8IOoz2q .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-UJLdSKOFN8IOoz2q .section-root text{fill:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-root span{color:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .section-2 span{color:#ffffff;}#mermaid-svg-UJLdSKOFN8IOoz2q .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-UJLdSKOFN8IOoz2q .edge{fill:none;}#mermaid-svg-UJLdSKOFN8IOoz2q .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-UJLdSKOFN8IOoz2q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ES6 Day02
数值系统
0b / 0o 字面量
Number 静态方法
Math 新方法
BigInt
数字分隔符 _
函数增强
默认参数
Rest 参数
箭头函数
尾逗号
标签模板
数组革新
扩展运算符 ...
Array.of / from
find / flat / at
对象现代化
属性与方法简写
计算属性名
super
对象展开
Object 新方法
【代码注释】本章四条主线(数值 → 函数 → 数组 → 对象)构成 Day02 完整知识脉络,建议按此顺序学习并在项目中逐项实践。
0.2 Day01 核心回顾(承上启下)
| 特性 | 要点 | 与 Day02 的关联 |
|---|---|---|
let / const |
块级作用域、暂时性死区、无变量提升 | 箭头函数、解构与默认参数都依赖块级语义 |
| 解构赋值 | 数组/对象模式匹配 | 与 Rest、扩展运算符、默认参数组合使用 |
| 模板字符串 | ````` 与 ${} 插值 |
标签模板在其之上增加自定义处理函数 |
| 字符串新方法 | includes、padStart 等 |
与数组 includes、at 形成对称 API 设计 |
0.3 核心名词百科
| 名词 | 英文 | 解析 |
|---|---|---|
| 字面量 | Literal | 在源码中直接写出的固定值,如 0b1010、45n |
| 安全整数 | Safe Integer | 在 -(2^53-1) 到 2^53-1 之间、可被 IEEE 754 精确表示的整数 |
| Rest 参数 | Rest Parameter | ...args 收集函数剩余 实参,得到真数组 |
| 扩展运算符 | Spread Operator | ...arr 展开可迭代对象,用于传参、拷贝、合并 |
| 箭头函数 | Arrow Function | 词法绑定 this,无 arguments,不能 new |
| 标签模板 | Tagged Template | 函数 + 模板字符串,首参为字符串片段数组 |
| 浅拷贝 | Shallow Copy | 只复制第一层引用;[...arr]、{...obj} 均为浅拷贝 |
| 遍历器 | Iterator | 实现 Symbol.iterator 的对象,可被 for...of 消费 |
| super | super | 在对象方法简写中指向当前对象的原型,与调用者无关 |
1. 引言:ES6+ 的现代化意义
ECMAScript 6(ES2015)及其后续版本(ES2017 尾逗号、ES2018 对象展开、ES2020 BigInt、ES2021 数字分隔符等)标志着 JavaScript 从「脚本语言」走向「工程化语言」。这些特性已被 React、Vue、Node.js、Webpack、Babel 等生态默认采用:组件写法、状态不可变更新、工具链转译,都建立在本文所讲解的语法之上。
业界典型落地:
- React :类组件方法、Hooks 回调普遍使用箭头函数;
setState用对象展开做浅合并。 - Redux / Zustand :
{ ...state, ...payload }实现不可变状态更新。 - Vue 3 :
setup中ref/reactive配合解构与 Rest 处理 props。 - lodash 替代 :原生
find、includes、flat减少第三方依赖。 - Node.js :
fs.promises、import()链式调用依赖箭头函数保持this。
2. 数值系统的新增特性
2.1 二进制与八进制表示法
ES6引入了更加直观的二进制和八进制数值表示方式,使得在处理底层编程和位运算时更加清晰和准确。
javascript
// 【代码注释】ES6 起可用 0b / 0o 前缀,在源码中直接写清进制,避免旧式 010 在严格模式下的歧义
const decimal = 255; // 十进制:无前缀,日常计数默认写法
const hexadecimal = 0xff; // 十六进制:ES5 已有,0x 前缀,常用于颜色、位掩码
const octal = 0o377; // 八进制:ES6 新增,0o 前缀(字母 o 避免与数字 0 混淆)
const binary = 0b11111111; // 二进制:ES6 新增,0b 前缀,常用于权限位、网络掩码
// 【代码注释】不同进制字面量只是「写法」不同,运行时都转为同一个 Number 值
console.log(binary); // 255 ------ 0b11111111 按二进制解析
console.log(octal); // 255 ------ 0o377 按八进制解析(3×8²+7×8+7)
【代码注释】ES6 用 0b / 0o 在源码层面标明进制,解析结果仍是普通 number。与 ES5 旧八进制 010 不同:严格模式下 010 可能报错或行为不一致,而 0o10 语义固定为八进制 8。三者 binary、octal、decimal 在此例中数值相等,便于理解「同一数值、多种字面量」。
名词解析 · 进制字面量 :0b 表示 binary(二进制),0o 表示 octal(八进制,字母 o 避免与数字 0 混淆),0x 表示 hexadecimal(十六进制)。严格模式下旧写法 010 可能被禁用,应优先使用 0o 前缀。
经典应用场景:
- 网络编程 :处理 IP 地址和子网掩码(如
0b11111111表示 255) - 图形处理 :颜色通道位运算(如
#FF→0xFF) - 权限位掩码:用二进制位表示开关组合
可运行示例 · 八进制与二进制表示法 (保存为 .html 后用浏览器打开控制台查看):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>八进制与二进制表示法</title>
</head>
<body>
<script>
'use strict';
const hex = 0x10; // 十六进制 → 16
const oct = 0o10; // 八进制 → 8
const bin = 0b10; // 二进制 → 2
console.log('十六进制 0x10 =', hex);
console.log('八进制 0o10 =', oct);
console.log('二进制 0b10 =', bin);
console.log('0b11111111 =', 0b11111111); // 255
console.log('0o377 =', 0o377); // 255
</script>
</body>
</html>
【代码注释】严格模式下应避免 010 这类歧义八进制;0o/0b 前缀使进制语义在源码层面一目了然。
2.2 Number构造函数的扩展
ES6在Number构造函数上新增了多个静态属性和方法,大大增强了数值处理能力。
2.2.1 数值范围与精度
javascript
// 【代码注释】安全整数:IEEE 754 双精度下可被「精确表示」的整数边界(±(2^53-1))
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991,即 2^53 - 1
console.log(Number.MIN_SAFE_INTEGER); // -(2^53 - 1),对称的下界
// 【代码注释】Number.EPSILON = 1 与「大于 1 的最小可表示浮点数」之差,是浮点比较的容差阈值
console.log(Number.EPSILON); // ≈ 2.22e-16
console.log(0.1 + 0.2 === 0.3); // false ------ 0.1、0.2 在二进制中无法精确存储,相加产生累积误差
// 【代码注释】正确做法:判断两数之差的绝对值是否小于 EPSILON,而非直接用 ===
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true
【代码注释】Number.EPSILON 是 ECMAScript 规定的「可接受浮点误差」单位:比较 a 与 b 是否「近似相等」应写 Math.abs(a - b) < Number.EPSILON,而不是 a === b。MAX_SAFE_INTEGER 之外的大整数会丢失精度(如 9007199254740992 === 9007199254740992 + 1 为 true),此时应改用 BigInt。
2.2.2 数值类型检测方法
javascript
// 【代码注释】Number.isNaN:仅当值为 NaN 时返回 true,不会对字符串做隐式转换
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('NaN')); // false ------ 全局 isNaN('NaN') 会先转数字再判断,结果为 true
console.log(Number.isNaN(100)); // false
// 【代码注释】Number.isFinite:必须是 number 类型且有限;字符串 '100' 不会先被转成数字
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite('100')); // false(全局 isFinite('100') 为 true)
// 【代码注释】Number.isInteger:值本身为整数,且为 number 类型(不含 '100' 字符串)
console.log(Number.isInteger(100)); // true
console.log(Number.isInteger(100.5)); // false
console.log(Number.isInteger('100')); // false
// 【代码注释】2^53 已超出安全整数,2^53-1 仍在安全范围内
console.log(Number.isSafeInteger(Math.pow(2, 53))); // false
console.log(Number.isSafeInteger(Math.pow(2, 53) - 1)); // true
// 【代码注释】ES6 将 parseInt/parseFloat 挂到 Number 上,行为与全局函数一致,便于模块化引用
console.log(Number.parseInt('100px')); // 100,从左解析到第一个非数字字符为止
console.log(Number.parseFloat('3.14abc')); // 3.14
console.log(Number.parseInt('0x1F')); // 31,识别十六进制前缀
console.log(Number.parseInt === parseInt); // true,同一函数引用,旧代码无需修改
【代码注释】ES6 的 Number.isXxx 系列遵循「无隐式类型转换」原则:参数必须先已是 number(isFinite/isInteger/isSafeInteger)或严格等于 NaN(isNaN),避免全局 isNaN/isFinite 把 'abc' 转成 NaN 再判断的陷阱。Number.parseInt 与 parseInt 是同一函数,迁移时只需改调用路径。表单校验推荐:Number.isFinite(Number(value)) 或配合 Number.isNaN。
名词解析 · Number.EPSILON :机器精度阈值,判断 0.1 + 0.2 === 0.3 时应使用 Math.abs(a - b) < Number.EPSILON,而非直接 ===。
深入理论 · IEEE 754 双精度浮点数
JavaScript 的 number 类型遵循 IEEE 754 64 位双精度标准,这决定了其精度上限与浮点误差的根本原因:
| 字段 | 位数 | 说明 |
|---|---|---|
| 符号位 | 1 bit | 决定正负 |
| 指数位 | 11 bits | 可表示约 ±1.7×10³⁰⁸ 的量级 |
| 尾数位 | 52 bits | 决定约 15--17 位十进制有效数字的精度 |
0.1 + 0.2 ≠ 0.3 的根本原因:十进制 0.1 在二进制中是无限循环小数(0.000110011001100...),存储时必须截断尾数;0.1 与 0.2 各自的截断误差相加后产生 0.30000000000000004,而非精确的 0.3。
javascript
// 【代码注释】金融场景三种策略:判等、展示、运算------前两种仍基于 number,第三种转整数彻底规避浮点
const isEqual = (a, b) => Math.abs(a - b) < Number.EPSILON; // 浮点判等:差值小于机器精度即视为相等
const toFixed2 = n => Math.round(n * 100) / 100; // 展示用:先放大 100 倍取整再缩小,减少显示误差
const centsAdd = (a, b) => (a * 100 + b * 100) / 100; // 运算用:元→分(整数)相加再÷100,避免 0.1+0.2 问题
console.log(isEqual(0.1 + 0.2, 0.3)); // true
console.log(toFixed2(0.1 + 0.2)); // 0.3
console.log(centsAdd(0.1, 0.2)); // 0.3
// 【代码注释】大额/高精度金额:以「分」为单位的 BigInt,加法无精度损失;展示时再 format 为元
const priceA = 10n; // 10 分 = 0.10 元
const priceB = 20n; // 20 分 = 0.20 元
console.log(String(priceA + priceB) + ' 分'); // '30 分' = 0.30 元
可运行示例 · Number 静态属性与方法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Number 构造函数扩展</title>
</head>
<body>
<script>
console.log('MAX_SAFE_INTEGER:', Number.MAX_SAFE_INTEGER);
console.log('MIN_SAFE_INTEGER:', Number.MIN_SAFE_INTEGER);
console.log('EPSILON:', Number.EPSILON);
console.log('isInteger(789):', Number.isInteger(789));
console.log('isInteger(78.9):', Number.isInteger(78.9));
const num = 9007199254740992;
console.log('isSafeInteger(2^53):', Number.isSafeInteger(num));
console.log('0.1+0.2===0.3:', 0.1 + 0.2 === 0.3);
console.log('近似相等:', Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);
</script>
</body>
</html>
【代码注释】9007199254740992 即 2^53,已超出安全整数范围,isSafeInteger 返回 false;金融计算应改用 BigInt 或专用库。
2.3 Math对象的增强
ES6为Math对象添加了多个新的数学计算方法,提高了数学运算的便捷性和可读性。
javascript
// 【代码注释】Math.trunc:向零取整,直接丢弃小数部分;与 floor 在负数时不同(trunc(-4.9)→-4,floor→-5)
console.log(Math.trunc(4.9)); // 4
console.log(Math.trunc(-4.9)); // -4
// 【代码注释】Math.sign:返回数值符号,正→1、负→-1、零→0、NaN→NaN;比手写 if 更清晰
console.log(Math.sign(5)); // 1
console.log(Math.sign(-5)); // -1
console.log(Math.sign(0)); // 0
console.log(Math.cbrt(27)); // 3:立方根 ∛27,ES6 新增
// 【代码注释】Math.hypot:√(a²+b²+...),内部优化避免 a*a+b*b 大数溢出,适合距离/向量长度
console.log(Math.hypot(3, 4)); // 5(勾股定理:3-4-5 直角三角形斜边)
【代码注释】Math.trunc 向零截断,Math.floor 向负无穷取整------处理负数时分清二者。Math.hypot(dx, dy) 是游戏/UI 中计算两点距离的惯用写法,比 Math.sqrt(dx*dx + dy*dy) 更安全。Math.sign 常用于动画方向判断(正反向速度)。
经典应用场景:
- 游戏开发 :
Math.hypot(dx, dy)计算两点距离 - 数据可视化:坐标缩放与向量长度
- 动画缓动 :
Math.sign判断运动方向
可运行示例 · Math 新增方法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Math 对象新增方法</title>
</head>
<body>
<script>
console.log('trunc(12.23):', Math.trunc(12.23));
console.log('sign(12.45):', Math.sign(12.45));
console.log('sign(-90):', Math.sign(-90));
console.log('sign(0):', Math.sign(0));
console.log('cbrt(8):', Math.cbrt(8));
console.log('hypot(3,4):', Math.hypot(3, 4)); // 5
console.log('hypot(3,4,5):', Math.hypot(3, 4, 5)); // √(9+16+25)
</script>
</body>
</html>
【代码注释】Math.trunc 向零取整;Math.floor 向负无穷取整,二者在负数时结果不同。Math.hypot 可避免 a*a + b*b 溢出。
2.4 BigInt:处理大整数
ES2020引入了BigInt类型,解决了JavaScript中Number类型无法精确表示大整数的问题。
javascript
// 【代码注释】三种创建方式:字面量后缀 n、BigInt(number)、BigInt(string)------超大数必须用字符串避免 number 先丢精度
const bigIntLiteral = 9007199254740991n; // 字面量:末尾加 n
const bigIntConstructor = BigInt(9007199254740991); // 从 number 转(须在安全整数内)
const bigIntString = BigInt('9007199254740991'); // 从字符串转(推荐处理超大 ID)
// 【代码注释】BigInt 同样支持 0b/0o/0x 进制字面量,后缀仍为 n
const binaryBigInt = 0b11111111111111111111111111111111n;
const octalBigInt = 0o777777777777777777777n;
const hexBigInt = 0xffffffffffffffffn;
// 【代码注释】BigInt 运算结果仍为 BigInt;9007199254740991n + 1n 可精确得到 9007199254740992n(number 做不到)
const largeNumber = 9007199254740991n;
const result = largeNumber + 1n;
console.log(result); // 9007199254740992n
// 【代码注释】禁止 BigInt 与 Number 混算,引擎不会自动转换,避免隐式精度问题
// const invalid = 100n + 100; // TypeError: Cannot mix BigInt and other types
const valid = 100n + BigInt(100); // 200n:显式统一为 BigInt
console.log(typeof 100n); // 'bigint':与 number 是两种不同的原始类型
【代码注释】BigInt 是 ES2020 新增的原始类型 (非 Number 子类型)。9007199254740991n + 1n 能精确得到 9007199254740992n,而用 number 时 9007199254740991 + 1 可能已不精确。混用 100n + 100 会 TypeError;比较时用 ===(56n === 56 为 false)。JSON 默认不支持 BigInt 序列化,需自定义 toJSON 或转字符串。
名词解析 · BigInt :ES2020 新增的原始类型 ,字面量后缀 n,typeof 返回 'bigint'。与 Number 混算会 TypeError,需显式 BigInt() 转换。
经典应用场景:
- 雪花 ID / Twitter Snowflake :超出
Number.MAX_SAFE_INTEGER的分布式 ID - 区块链:大整数余额与哈希运算
- 高精度计数器:点赞、播放量等超大整数
可运行示例 · BigInt:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>BigInt 大整数</title>
</head>
<body>
<script>
const n1 = 56n;
const n2 = 0b101n;
const n3 = 0o75n;
const n4 = 0xab1n;
console.log(n1, typeof n1);
console.log('56n + 10n =', n1 + 10n);
console.log('进制:', n2, n3, n4);
const num = 56;
console.log('===', n1 === num, '==', n1 == num);
console.log('转 Number:', Number(n1));
</script>
</body>
</html>
【代码注释】56n === 56 为 false(类型不同);== 会做类型转换可能为 true,比较 BigInt 建议用 === 或先统一类型。
2.5 数字分隔符
ES2021引入的数字分隔符极大地提高了大数字的可读性。
javascript
// 【代码注释】下划线 _ 仅用于源码可读性,编译后与普通数字完全等价;运行时输出不含 _
const billion = 1_000_000_000; // 十亿,按千分位分组
const bytes = 0xff_ff_ff_ff; // 十六进制按字节分组(每 2 位一字节)
const bits = 0b1010_0001_1000_0101; // 二进制按 4 位(半字节)分组
const maxSafeInteger = 9007199254740_991; // 安全整数上界,分段便于核对
// 【代码注释】小数、大整数均可使用;禁止在开头/结尾/连续出现 __
const price = 12_999.99; // 12999.99
const phoneNumber = 138_0013_8000; // 13800138000(仅视觉分组,非字符串)
const creditCard = 1234_5678_9012_3456; // 16 位卡号分段
console.log(billion); // 1000000000 ------ 引擎存储与打印时忽略 _
【代码注释】数字分隔符(ES2021)只存在于源码 ,JSON.parse('1_000') 和 Number('12_000') 均报错。规则:_ 可插在数字任意中间位置,不能首尾、不能连续 __、不能紧邻小数点两侧。适用:金额常量、超时毫秒、二进制掩码、BigInt 大数。
经典应用场景:
- 财务应用 :
const price = 12_999.99 - 配置常量 :
const TIMEOUT_MS = 3_600_000 - 二进制掩码 :
0b1010_0001_1000_0101
可运行示例 · 数字分隔符:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>数字分隔符</title>
</head>
<body>
<script>
const a = 123_0000;
const b = 12_0000_0000;
const c = 12_434_900;
console.log(a, b, c);
console.log('运行时无下划线:', typeof a === 'number');
</script>
</body>
</html>
【代码注释】_ 仅提升源码可读性,JSON.parse 与 Number('12_000') 均不支持分隔符。
2.6 数值模块知识点归纳
#mermaid-svg-lBD2EMaPuZsayjD4{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-lBD2EMaPuZsayjD4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lBD2EMaPuZsayjD4 .error-icon{fill:#552222;}#mermaid-svg-lBD2EMaPuZsayjD4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lBD2EMaPuZsayjD4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lBD2EMaPuZsayjD4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lBD2EMaPuZsayjD4 .marker.cross{stroke:#333333;}#mermaid-svg-lBD2EMaPuZsayjD4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lBD2EMaPuZsayjD4 p{margin:0;}#mermaid-svg-lBD2EMaPuZsayjD4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster-label text{fill:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster-label span{color:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster-label span p{background-color:transparent;}#mermaid-svg-lBD2EMaPuZsayjD4 .label text,#mermaid-svg-lBD2EMaPuZsayjD4 span{fill:#333;color:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 .node rect,#mermaid-svg-lBD2EMaPuZsayjD4 .node circle,#mermaid-svg-lBD2EMaPuZsayjD4 .node ellipse,#mermaid-svg-lBD2EMaPuZsayjD4 .node polygon,#mermaid-svg-lBD2EMaPuZsayjD4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lBD2EMaPuZsayjD4 .rough-node .label text,#mermaid-svg-lBD2EMaPuZsayjD4 .node .label text,#mermaid-svg-lBD2EMaPuZsayjD4 .image-shape .label,#mermaid-svg-lBD2EMaPuZsayjD4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-lBD2EMaPuZsayjD4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lBD2EMaPuZsayjD4 .rough-node .label,#mermaid-svg-lBD2EMaPuZsayjD4 .node .label,#mermaid-svg-lBD2EMaPuZsayjD4 .image-shape .label,#mermaid-svg-lBD2EMaPuZsayjD4 .icon-shape .label{text-align:center;}#mermaid-svg-lBD2EMaPuZsayjD4 .node.clickable{cursor:pointer;}#mermaid-svg-lBD2EMaPuZsayjD4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lBD2EMaPuZsayjD4 .arrowheadPath{fill:#333333;}#mermaid-svg-lBD2EMaPuZsayjD4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lBD2EMaPuZsayjD4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lBD2EMaPuZsayjD4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lBD2EMaPuZsayjD4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lBD2EMaPuZsayjD4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lBD2EMaPuZsayjD4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster text{fill:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 .cluster span{color:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 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-lBD2EMaPuZsayjD4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lBD2EMaPuZsayjD4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-lBD2EMaPuZsayjD4 .icon-shape,#mermaid-svg-lBD2EMaPuZsayjD4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lBD2EMaPuZsayjD4 .icon-shape p,#mermaid-svg-lBD2EMaPuZsayjD4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lBD2EMaPuZsayjD4 .icon-shape .label rect,#mermaid-svg-lBD2EMaPuZsayjD4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lBD2EMaPuZsayjD4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lBD2EMaPuZsayjD4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lBD2EMaPuZsayjD4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
数值问题
是否超 2^53-1?
BigInt
是否浮点比较?
Number.EPSILON
Number.isInteger / isSafeInteger
【代码注释】数值选型决策树------超安全整数用 BigInt,浮点比较用 Number.EPSILON,整数判定用 Number.isInteger / isSafeInteger,避免混用导致精度问题。
| API / 语法 | 作用 | 与 ES5 差异 |
|---|---|---|
0b / 0o |
二/八进制字面量 | 语义明确,严格模式友好 |
Number.EPSILON |
浮点误差容差 | ES5 无 |
Number.isNaN |
严格 NaN 检测 | 不对 'NaN' 字符串真 |
BigInt / n |
任意精度整数 | ES2020 |
1_000_000 |
数字分隔符 | ES2021,仅源码 |
3. 函数增强特性
3.1 参数默认值
ES6提供了更简洁的函数参数默认值语法,比传统的检查方式更加优雅。
javascript
// 【代码注释】ES5 用 || 赋默认值:会把 0、''、false 误判为「未传参」,应改用 ES6 默认参数
function greetES5(name) {
name = name || 'Guest'; // 若 name 为 '' 也会变成 'Guest'
console.log('Hello, ' + name);
}
// 【代码注释】ES6 形参列表直接写 = 默认值:仅当实参为 undefined 时生效(null 不会触发)
function greetES6(name = 'Guest', age = 18) {
console.log(`Hello, ${name}, age: ${age}`);
}
// 【代码注释】默认值可以是表达式,且可引用前面已声明的形参(按从左到右顺序求值)
function createUser(name = 'Anonymous', role = 'user', permissions = role === 'admin' ? ['read', 'write', 'delete'] : ['read']) {
return { name, role, permissions };
}
// 【代码注释】参数默认值 + 解构:= [] 防止未传参时解构 undefined;third = 0 为解构内的元素默认值
function processArray([first, second, third = 0] = []) {
console.log(first, second, third);
}
processArray([1, 2]); // 1 2 0 ------ third 缺省为 0
【代码注释】默认参数在每次调用且该参数值为 undefined 时求值(null 不会触发)。默认值表达式可引用左侧形参(如 permissions 依赖 role)。与解构组合时常见写法 function f({ x = 1 } = {}):外层 = {} 防止未传对象,内层 x = 1 防止属性缺失。勿用 || 代替默认参数,以免把合法的 0、'' 覆盖掉。
名词解析 · 默认参数 :形参列表中的 = 右侧在每次调用且该参数为 undefined 时 求值;传入 null 不会触发默认值。
可运行示例 · 参数默认值:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>函数默认参数</title>
</head>
<body>
<script>
function greet(name = 'Guest', age = 18) {
console.log(`Hello, ${name}, age: ${age}`);
}
greet();
greet('小明', 22);
function sum(a, b = 0, c = 0) {
return a + b + c;
}
console.log('sum(10):', sum(10));
console.log('sum(10,20):', sum(10, 20));
</script>
</body>
</html>
【代码注释】默认参数与解构、Rest 常组合:function f({ x = 1 } = {}) 可同时提供对象默认值与属性默认值。
3.2 Rest参数:收集剩余参数
Rest参数提供了一种更优雅的方式来处理函数中的可变参数,替代了传统的arguments对象。
javascript
// 【代码注释】Rest 参数 ...numbers:收集所有剩余实参为真数组,可直接 reduce/map,替代 arguments
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// 【代码注释】Rest 必须位于形参列表最后;前面固定参数 name、age,后面全部进入 hobbies 数组
function createUser(name, age, ...hobbies) {
return {
name,
age,
hobbies: hobbies.length > 0 ? hobbies : ['无'] // 无额外爱好时给默认文案
};
}
console.log(createUser('张三', 25, '读书', '游泳', '编程'));
// { name: '张三', age: 25, hobbies: ['读书', '游泳', '编程'] }
// 【代码注释】对象方法简写中同样支持 Rest;average 内用扩展运算符把 numbers 再展开传给 add
const calculator = {
add(...numbers) {
return numbers.reduce((sum, num) => sum + num, 0);
},
average(...numbers) {
if (numbers.length === 0) return 0;
return this.add(...numbers) / numbers.length; // 展开 + 收集:同一 ... 语法,位置决定语义
}
};
console.log(calculator.average(1, 2, 3, 4, 5)); // 3
【代码注释】Rest 与 arguments 对比:Rest 是真数组、只含「剩余」参数、箭头函数可用;arguments 含全部实参、类数组、箭头函数无。Rest 不能用于箭头函数形参以外的「单独声明」。与扩展运算符同写 ...:形参/解构左侧 = 收集 ,调用/字面量右侧 = 展开。
Rest参数与arguments的区别:
#mermaid-svg-rpb1F8ObAtay5u7f{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-rpb1F8ObAtay5u7f .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rpb1F8ObAtay5u7f .error-icon{fill:#552222;}#mermaid-svg-rpb1F8ObAtay5u7f .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rpb1F8ObAtay5u7f .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rpb1F8ObAtay5u7f .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rpb1F8ObAtay5u7f .marker.cross{stroke:#333333;}#mermaid-svg-rpb1F8ObAtay5u7f svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rpb1F8ObAtay5u7f p{margin:0;}#mermaid-svg-rpb1F8ObAtay5u7f .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rpb1F8ObAtay5u7f .cluster-label text{fill:#333;}#mermaid-svg-rpb1F8ObAtay5u7f .cluster-label span{color:#333;}#mermaid-svg-rpb1F8ObAtay5u7f .cluster-label span p{background-color:transparent;}#mermaid-svg-rpb1F8ObAtay5u7f .label text,#mermaid-svg-rpb1F8ObAtay5u7f span{fill:#333;color:#333;}#mermaid-svg-rpb1F8ObAtay5u7f .node rect,#mermaid-svg-rpb1F8ObAtay5u7f .node circle,#mermaid-svg-rpb1F8ObAtay5u7f .node ellipse,#mermaid-svg-rpb1F8ObAtay5u7f .node polygon,#mermaid-svg-rpb1F8ObAtay5u7f .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rpb1F8ObAtay5u7f .rough-node .label text,#mermaid-svg-rpb1F8ObAtay5u7f .node .label text,#mermaid-svg-rpb1F8ObAtay5u7f .image-shape .label,#mermaid-svg-rpb1F8ObAtay5u7f .icon-shape .label{text-anchor:middle;}#mermaid-svg-rpb1F8ObAtay5u7f .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rpb1F8ObAtay5u7f .rough-node .label,#mermaid-svg-rpb1F8ObAtay5u7f .node .label,#mermaid-svg-rpb1F8ObAtay5u7f .image-shape .label,#mermaid-svg-rpb1F8ObAtay5u7f .icon-shape .label{text-align:center;}#mermaid-svg-rpb1F8ObAtay5u7f .node.clickable{cursor:pointer;}#mermaid-svg-rpb1F8ObAtay5u7f .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rpb1F8ObAtay5u7f .arrowheadPath{fill:#333333;}#mermaid-svg-rpb1F8ObAtay5u7f .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rpb1F8ObAtay5u7f .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rpb1F8ObAtay5u7f .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rpb1F8ObAtay5u7f .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rpb1F8ObAtay5u7f .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rpb1F8ObAtay5u7f .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rpb1F8ObAtay5u7f .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rpb1F8ObAtay5u7f .cluster text{fill:#333;}#mermaid-svg-rpb1F8ObAtay5u7f .cluster span{color:#333;}#mermaid-svg-rpb1F8ObAtay5u7f 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-rpb1F8ObAtay5u7f .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rpb1F8ObAtay5u7f rect.text{fill:none;stroke-width:0;}#mermaid-svg-rpb1F8ObAtay5u7f .icon-shape,#mermaid-svg-rpb1F8ObAtay5u7f .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rpb1F8ObAtay5u7f .icon-shape p,#mermaid-svg-rpb1F8ObAtay5u7f .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rpb1F8ObAtay5u7f .icon-shape .label rect,#mermaid-svg-rpb1F8ObAtay5u7f .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rpb1F8ObAtay5u7f .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rpb1F8ObAtay5u7f .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rpb1F8ObAtay5u7f :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 函数参数收集
arguments对象
Rest参数
类数组对象
包含所有参数
不支持数组方法
真正的数组
只收集剩余参数
支持所有数组方法
【代码注释】Rest 参数得到真数组,可直接 map/reduce;arguments 是类数组需转换,且包含全部实参而非「剩余」部分。新代码应优先 Rest。
经典应用场景:
- 可变参数函数 :
Math.max(...nums)的逆操作------收集实参 - Express 中间件 :
(req, res, ...handlers)模式 - 日志聚合 :
function log(level, ...messages)
可运行示例 · Rest 参数:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Rest 参数</title>
</head>
<body>
<script>
function fn01(...args) {
console.log('纯数组:', args, Array.isArray(args));
}
fn01(100, 200, 250, '小明', true);
function fn02(name, age, ...data) {
console.log(name, age, 'rest:', data);
}
fn02('小明', 101, '甲', '乙', '丙');
function sum(...nums) {
return nums.reduce((prev, item) => prev + item, 0);
}
console.log('sum:', sum(10, 20, 30, 40, 50, 60));
</script>
</body>
</html>
【代码注释】Rest 与扩展运算符写法相同(...),在形参位置为收集,在值位置为展开------合称「展开/收集语法」。
3.3 箭头函数:简洁与this绑定
箭头函数是ES6中最具革命性的特性之一,它不仅提供了更简洁的语法,还解决了JavaScript中长期存在的this绑定问题。
3.3.1 箭头函数的语法演变
javascript
// 【代码注释】传统函数声明:有函数名、function 关键字、可用 new 构造、有 arguments 对象、有声明提升
function add(a, b) {
return a + b;
}
// 【代码注释】箭头函数完整形式:多参数须加圆括号,多语句须加 {} 和 return
const add1 = (a, b) => {
return a + b;
};
// 【代码注释】单参数可省略圆括号(仅一个参数时;零参数或多参数均需括号)
const double = n => {
return n * 2;
};
// 【代码注释】单表达式可省略 {} 与 return:表达式值即为返回值(隐式 return)
// 等价于:const add2 = (a, b) => { return a + b; }
const add2 = (a, b) => a + b;
// 【代码注释】返回对象字面量时必须用 () 包裹:
// 否则引擎将 {} 解析为函数体(空语句),而不是对象字面量
// ❌ 错误:const f = () => { name, age }; → 返回 undefined
// ✅ 正确:() => ({ name, age }) → 返回对象
const createUser = (name, age) => ({
name, // ES6 属性简写:等价于 name: name
age
});
// 【代码注释】箭头函数 IIFE(立即执行函数表达式)
// 外层 () 让引擎将函数识别为表达式而非语句,尾部 () 立即调用
// 常见于模块化初始化与作用域隔离
((name) => {
console.log(`Hello, ${name}`);
})('World');
// 【代码注释】无参数箭头函数:空括号不可省略
const sayHello = () => console.log('Hello!');
// 【代码注释】多行异步箭头函数:async/await 与箭头函数天然搭配
const fetchUser = async (id) => {
const res = await fetch(`/api/users/${id}`);
return res.json(); // 返回 Promise,调用方 await 即可
};
【代码注释】箭头函数的语法变化提供了从详细到简洁的多种选择,开发者可以根据代码的可读性要求选择合适的语法形式。记忆口诀:「单参省括号,单行省 return,返对象加括号,this 看外层」。
3.3.2 箭头函数的this特性
箭头函数最重要的特性是它没有自己的this,而是继承自外层作用域。
javascript
// 传统函数的this问题
const user = {
name: '张三',
friends: ['李四', '王五', '赵六'],
// 传统函数:this指向调用者
printFriendsTraditional: function() {
this.friends.forEach(function(friend) {
console.log(this.name + ' 的朋友: ' + friend); // this丢失
});
},
// 箭头函数:this继承自外层
printFriendsArrow: function() {
this.friends.forEach(friend => {
console.log(this.name + ' 的朋友: ' + friend); // this正确
});
},
// 对象方法简写
printFriendsConcise() {
this.friends.forEach(friend => {
console.log(this.name + ' 的朋友: ' + friend);
});
}
};
// DOM操作中的this
class ButtonHandler {
constructor(buttonId) {
this.button = document.getElementById(buttonId);
this.count = 0;
// 传统函数需要bind
this.handleClickTraditional = function() {
this.count++;
console.log('点击次数:', this.count);
}.bind(this);
// 箭头函数自动继承this
this.handleClickArrow = () => {
this.count++;
console.log('点击次数:', this.count);
};
this.button.addEventListener('click', this.handleClickArrow);
}
}
【代码注释】箭头函数的this绑定是静态的,在函数定义时确定,而不是在调用时确定。这解决了JavaScript中this绑定混乱的问题。
箭头函数this绑定机制:
#mermaid-svg-yibJLB8yH8knlsXa{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-yibJLB8yH8knlsXa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yibJLB8yH8knlsXa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yibJLB8yH8knlsXa .error-icon{fill:#552222;}#mermaid-svg-yibJLB8yH8knlsXa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yibJLB8yH8knlsXa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yibJLB8yH8knlsXa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yibJLB8yH8knlsXa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yibJLB8yH8knlsXa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yibJLB8yH8knlsXa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yibJLB8yH8knlsXa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yibJLB8yH8knlsXa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yibJLB8yH8knlsXa .marker.cross{stroke:#333333;}#mermaid-svg-yibJLB8yH8knlsXa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yibJLB8yH8knlsXa p{margin:0;}#mermaid-svg-yibJLB8yH8knlsXa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yibJLB8yH8knlsXa .cluster-label text{fill:#333;}#mermaid-svg-yibJLB8yH8knlsXa .cluster-label span{color:#333;}#mermaid-svg-yibJLB8yH8knlsXa .cluster-label span p{background-color:transparent;}#mermaid-svg-yibJLB8yH8knlsXa .label text,#mermaid-svg-yibJLB8yH8knlsXa span{fill:#333;color:#333;}#mermaid-svg-yibJLB8yH8knlsXa .node rect,#mermaid-svg-yibJLB8yH8knlsXa .node circle,#mermaid-svg-yibJLB8yH8knlsXa .node ellipse,#mermaid-svg-yibJLB8yH8knlsXa .node polygon,#mermaid-svg-yibJLB8yH8knlsXa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yibJLB8yH8knlsXa .rough-node .label text,#mermaid-svg-yibJLB8yH8knlsXa .node .label text,#mermaid-svg-yibJLB8yH8knlsXa .image-shape .label,#mermaid-svg-yibJLB8yH8knlsXa .icon-shape .label{text-anchor:middle;}#mermaid-svg-yibJLB8yH8knlsXa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yibJLB8yH8knlsXa .rough-node .label,#mermaid-svg-yibJLB8yH8knlsXa .node .label,#mermaid-svg-yibJLB8yH8knlsXa .image-shape .label,#mermaid-svg-yibJLB8yH8knlsXa .icon-shape .label{text-align:center;}#mermaid-svg-yibJLB8yH8knlsXa .node.clickable{cursor:pointer;}#mermaid-svg-yibJLB8yH8knlsXa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yibJLB8yH8knlsXa .arrowheadPath{fill:#333333;}#mermaid-svg-yibJLB8yH8knlsXa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yibJLB8yH8knlsXa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yibJLB8yH8knlsXa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yibJLB8yH8knlsXa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yibJLB8yH8knlsXa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yibJLB8yH8knlsXa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yibJLB8yH8knlsXa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yibJLB8yH8knlsXa .cluster text{fill:#333;}#mermaid-svg-yibJLB8yH8knlsXa .cluster span{color:#333;}#mermaid-svg-yibJLB8yH8knlsXa 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-yibJLB8yH8knlsXa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yibJLB8yH8knlsXa rect.text{fill:none;stroke-width:0;}#mermaid-svg-yibJLB8yH8knlsXa .icon-shape,#mermaid-svg-yibJLB8yH8knlsXa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yibJLB8yH8knlsXa .icon-shape p,#mermaid-svg-yibJLB8yH8knlsXa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yibJLB8yH8knlsXa .icon-shape .label rect,#mermaid-svg-yibJLB8yH8knlsXa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yibJLB8yH8knlsXa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yibJLB8yH8knlsXa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yibJLB8yH8knlsXa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 箭头函数定义
查找外层作用域
找到this绑定
静态绑定this
继承外层this
无法通过call/apply改变
【代码注释】箭头函数 this 在定义时沿作用域链确定,与调用方式无关;call/apply/bind 无法改变。对象方法、构造函数、需动态 this 的场景应使用普通函数。
3.3.3 箭头函数的适用场景
javascript
// 【代码注释】场景 1:高阶方法回调 ------ 箭头函数无自己的 this,只关心参数,写法最简
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // 每个元素 ×2,返回新数组
const evenNumbers = numbers.filter(n => n % 2 === 0); // 保留偶数
// 【代码注释】场景 2:Promise 链 ------ 箭头避免在 then 里丢失外层 this,且单表达式可省 return
fetch('/api/user')
.then(response => response.json()) // 解析 JSON,返回 Promise
.then(user => console.log(user)) // 上一 then 的结果作为 user
.catch(error => console.error(error));
// 【代码注释】场景 3:类组件 ------ 箭头作为实例属性,this 永久绑定到组件实例,无需 constructor 里 bind
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
this.handleClick = () => {
this.setState({ loading: false }); // this 始终指向当前 UserProfile 实例
};
}
render() {
return <button onClick={this.handleClick}>点击</button>;
}
}
// 【代码注释】场景 4:对象方法 ------ sum 用简写,this 指向 mathUtils;average 用箭头则 this 指向外层(非 mathUtils)
const mathUtils = {
numbers: [1, 2, 3, 4, 5],
sum() {
return this.numbers.reduce((total, num) => total + num, 0); // 方法简写:this === mathUtils
},
average: () => {
// 箭头在对象字面量中不绑定 this,只能写死 mathUtils 或改用 average() { ... }
return mathUtils.numbers.reduce((total, num) => total + num, 0) / mathUtils.numbers.length;
}
};
【代码注释】箭头函数适用清单:纯回调 (map/filter/then)、需要固定外层 this (类组件事件、定时器内访问实例)。禁用清单:对象字面量的方法 (应写 method() {})、构造函数 、需要 arguments 的函数 、Generator 。average: () => 的 this 在浏览器中常指向 window,因此示例里用 mathUtils.numbers 硬编码;正确写法是 average() { return this.numbers... }。
箭头函数 vs 普通函数:完整特性对比
| 特性 | 普通函数 | 箭头函数 |
|---|---|---|
this 绑定 |
调用时动态确定 | 定义时词法继承(静态) |
arguments 对象 |
✅ 有 | ❌ 无(用 Rest 替代) |
new 构造调用 |
✅ 可作构造函数 | ❌ TypeError |
prototype 属性 |
✅ 有 | ❌ 无 |
super 关键字 |
✅ 方法简写中可用 | ❌ 无 |
yield(Generator) |
✅ 可声明 function* |
❌ 无 |
call/apply/bind 改变 this |
✅ 有效 | ❌ 无效(this 已固定) |
| 函数声明提升 | ✅ function f(){} 提升 |
❌ 表达式不提升 |
| 适用场景 | 对象方法、构造函数、需要动态 this 的事件回调 |
纯函数、高阶回调、Promise 链 |
javascript
// 验证:箭头函数无 arguments,需用 Rest
const fn = (...args) => args.reduce((a, b) => a + b, 0);
// 验证:箭头函数不能 new
const Arrow = () => {};
// new Arrow(); // TypeError: Arrow is not a constructor
// 验证:bind 无法改变箭头函数 this
const obj = { name: '对象' };
const arrow = () => this;
const bound = arrow.bind(obj);
console.log(bound() === obj); // false(this 仍是定义时的 this)
经典应用场景:
- React:类组件事件处理、Hooks 回调
- Vue 3 :
computed(() => ...)、watch回调 - 数组高阶方法 :
users.filter(u => u.age >= 18)
可运行示例 · 箭头函数语法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>箭头函数语法</title>
</head>
<body>
<script>
const fn01 = () => {};
const fn02 = (name, age) => console.log(`我叫${name},${age}岁`);
const fn03 = num => console.log(num * 2 + 100);
const fn04 = (n1, n2) => n1 * n2;
const fn05 = item => item * item;
fn02('小明', 101);
console.log(fn04(100, 200), fn05(5));
</script>
</body>
</html>
【代码注释】返回对象字面量须加括号:() => ({ id: 1 }),否则 {} 会被解析为函数体。
可运行示例 · 箭头函数 this 特性(含 DOM,请在浏览器中打开):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>箭头函数 this</title>
</head>
<body>
<button id="btn">点我</button>
<ul id="news">
<li>条目 1</li><li>条目 2</li><li>条目 3</li>
</ul>
<script>
const user = {
name: '小明',
getInfo: () => console.log('对象方法箭头 this →', this)
};
user.getInfo();
document.querySelector('#btn').onclick = () =>
console.log('按钮箭头 this →', this);
document.querySelectorAll('#news li').forEach(li => {
li.onclick = () => console.log('li 箭头 this →', this);
});
const obj = {
getInfo() {
const fn = () => console.log('嵌套箭头 this →', this.name);
fn();
}
};
obj.getInfo();
</script>
</body>
</html>
【代码注释】对象字面量中的 getInfo: () => 的 this 指向定义时的外层(常为 window),对象方法应使用简写 getInfo() {}。
可运行示例 · 箭头函数在数组中的应用:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>箭头函数应用</title>
</head>
<body>
<script>
const users = [
{ name: '刘备', age: 45, address: '上海' },
{ name: '关羽', age: 43, address: '苏州' },
{ name: '张飞', age: 41, address: '无锡' },
{ name: '诸葛亮', age: 44, address: '杭州' },
{ name: '曹操', age: 54, address: '南京' }
];
users.sort((a, b) => a.age - b.age);
console.log('排序后:', users);
console.log('40岁以上:', users.filter(item => item.age >= 40));
console.log('姓名列表:', users.map(item => item.name));
</script>
</body>
</html>
【代码注释】sort 比较函数若用箭头函数,无法访问 sort 的 this 参数(一般不需要);比较时建议 (a, b) => a.age - b.age。
3.4 函数参数尾逗号
ES2017允许函数定义和调用时在最后一个参数后面添加逗号,提高了代码的可维护性。
javascript
// 函数定义时的尾逗号
function createUser(
name,
age,
email, // 尾逗号
) {
return { name, age, email };
}
// 函数调用时的尾逗号
const user = createUser(
'张三',
25,
'zhangsan@example.com', // 尾逗号
);
// 与版本控制系统配合
const config = {
host: 'localhost',
port: 3000,
database: 'myapp', // 添加新属性时不会影响前面的行
};
// 数组解构中的尾逗号
const [first, second, third,] = [1, 2, 3, 4];
【代码注释】参数尾逗号使得在版本控制系统中添加、删除或重排参数时diff更加清晰,减少了无关行的变化。
3.5 标签模板字符串
标签模板是模板字符串的高级应用,允许开发者自定义模板字符串的处理逻辑。
javascript
// 基本标签模板语法
function simpleTag(strings, ...values) {
console.log('字符串数组:', strings);
console.log('表达式值:', values);
return '处理结果';
}
const name = '张三';
const age = 25;
simpleTag`我叫${name},今年${age}岁`;
// 实际应用:HTML转义
function safeHtml(strings, ...values) {
const escaped = values.map(value => {
if (typeof value === 'string') {
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return value;
});
return strings.reduce((result, string, i) => {
return result + string + (escaped[i] || '');
}, '');
}
const userInput = '<script>alert("XSS")</script>';
const html = safeHtml`<div>${userInput}</div>`;
console.log(html); // <div><script>alert("XSS")</script></div>
// 国际化应用
function i18nTag(strings, ...values) {
const locale = navigator.language || 'en-US';
// 根据locale选择不同的翻译
const translations = {
'zh-CN': {
'Hello': '你好',
'Welcome': '欢迎'
}
};
return strings.reduce((result, string, i) => {
const translatedString = translations[locale][string] || string;
return result + translatedString + (values[i] || '');
}, '');
}
// 样式化输出
function styleTag(strings, ...values) {
const styles = ['color: red', 'font-weight: bold', 'font-size: 20px'];
return strings.reduce((result, string, i) => {
return result + `%c${string}` + (values[i] || '');
}, '');
}
console.log(styleTag`重要提示 ${'不要关闭浏览器'}`);
【代码注释】标签模板的第一个参数是字符串数组,包含模板字符串中的静态部分;后续参数是表达式的计算结果。标签模板可以用于实现DSL(领域特定语言)。
内置标签函数 · String.raw
String.raw 是 ES6 内置的标签模板函数,让反斜杠不做任何转义处理,直接返回原始字符串,是日常开发中最常用的内置标签函数:
javascript
// 普通模板:\n → 换行,\t → 制表符
console.log(`第一行\n第二行`); // 输出两行
// String.raw:完整保留 \n \t 字符本身
console.log(String.raw`第一行\n第二行`); // 第一行\n第二行(单行)
// 实用场景 1:Windows 文件路径(避免 \\ 双写)
const filePath = String.raw`C:\Users\admin\Documents\report.xlsx`;
console.log(filePath); // C:\Users\admin\Documents\report.xlsx
// 实用场景 2:正则表达式字符串(避免双重转义)
const pattern = new RegExp(String.raw`^\d{3}-\d{4}$`);
// 等价于 new RegExp('^\\d{3}-\\d{4}$')(省去大量 \\)
console.log(pattern.test('010-1234')); // true
// 原理:String.raw 读取 strings.raw(存储未转义的原始字符串片段)
function myRaw(strings, ...values) {
return strings.raw.reduce((acc, str, i) =>
acc + (values[i - 1] !== undefined ? values[i - 1] : '') + str
);
}
const a = 1, b = 2;
console.log(myRaw`${a} + ${b} = \n结果`); // 1 + 2 = \n结果
经典应用场景:
- styled-components :``css`color: ${c}``` 标签函数处理样式
- lit-html:模板安全渲染
- i18n:按 locale 替换静态片段
String.raw:正则源字符串、Windows 路径、避免手写双重反斜杠
可运行示例 · 标签模板:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>标签模板</title>
</head>
<body>
<script>
function func(strings, ...values) {
console.log('strings:', strings);
console.log('values:', values);
}
func``;
func`Hello World`;
const a = 100, b = 200;
func`Hello ${a} World ${b}`;
</script>
</body>
</html>
【代码注释】func\Hello a W o r l d {a}World aWorld{b}`等价于func('Hello ', 'World ', '', 100, 200);strings.raw` 可保留原始转义。
3.6 函数模块知识点归纳
| 特性 | 语法要点 | 不宜使用场景 |
|---|---|---|
| 默认参数 | x = 1 |
--- |
| Rest | ...args 必须在最后 |
与扩展混淆时注意位置 |
| 箭头函数 | 无 this/arguments/new |
对象方法、构造函数、需要动态 this 时 |
| 尾逗号 | 参数列表末尾 , |
--- |
| 标签模板 | tag\...`` |
简单拼接用普通模板字符串即可 |
#mermaid-svg-5ubY4NrBAVfKCD48{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-5ubY4NrBAVfKCD48 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5ubY4NrBAVfKCD48 .error-icon{fill:#552222;}#mermaid-svg-5ubY4NrBAVfKCD48 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5ubY4NrBAVfKCD48 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5ubY4NrBAVfKCD48 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5ubY4NrBAVfKCD48 .marker.cross{stroke:#333333;}#mermaid-svg-5ubY4NrBAVfKCD48 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5ubY4NrBAVfKCD48 p{margin:0;}#mermaid-svg-5ubY4NrBAVfKCD48 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster-label text{fill:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster-label span{color:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster-label span p{background-color:transparent;}#mermaid-svg-5ubY4NrBAVfKCD48 .label text,#mermaid-svg-5ubY4NrBAVfKCD48 span{fill:#333;color:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 .node rect,#mermaid-svg-5ubY4NrBAVfKCD48 .node circle,#mermaid-svg-5ubY4NrBAVfKCD48 .node ellipse,#mermaid-svg-5ubY4NrBAVfKCD48 .node polygon,#mermaid-svg-5ubY4NrBAVfKCD48 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5ubY4NrBAVfKCD48 .rough-node .label text,#mermaid-svg-5ubY4NrBAVfKCD48 .node .label text,#mermaid-svg-5ubY4NrBAVfKCD48 .image-shape .label,#mermaid-svg-5ubY4NrBAVfKCD48 .icon-shape .label{text-anchor:middle;}#mermaid-svg-5ubY4NrBAVfKCD48 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5ubY4NrBAVfKCD48 .rough-node .label,#mermaid-svg-5ubY4NrBAVfKCD48 .node .label,#mermaid-svg-5ubY4NrBAVfKCD48 .image-shape .label,#mermaid-svg-5ubY4NrBAVfKCD48 .icon-shape .label{text-align:center;}#mermaid-svg-5ubY4NrBAVfKCD48 .node.clickable{cursor:pointer;}#mermaid-svg-5ubY4NrBAVfKCD48 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5ubY4NrBAVfKCD48 .arrowheadPath{fill:#333333;}#mermaid-svg-5ubY4NrBAVfKCD48 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5ubY4NrBAVfKCD48 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5ubY4NrBAVfKCD48 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ubY4NrBAVfKCD48 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5ubY4NrBAVfKCD48 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ubY4NrBAVfKCD48 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster text{fill:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 .cluster span{color:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 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-5ubY4NrBAVfKCD48 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5ubY4NrBAVfKCD48 rect.text{fill:none;stroke-width:0;}#mermaid-svg-5ubY4NrBAVfKCD48 .icon-shape,#mermaid-svg-5ubY4NrBAVfKCD48 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ubY4NrBAVfKCD48 .icon-shape p,#mermaid-svg-5ubY4NrBAVfKCD48 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5ubY4NrBAVfKCD48 .icon-shape .label rect,#mermaid-svg-5ubY4NrBAVfKCD48 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ubY4NrBAVfKCD48 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5ubY4NrBAVfKCD48 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5ubY4NrBAVfKCD48 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 展开与收集
展开
收集
... 在调用处
func...arr
... 在形参处
...rest
【代码注释】... 语法相同、位置不同------在调用/字面量 处为展开(spread),在形参/解构处为收集(rest)。记忆:往外「摊」是展开,往里「收」是 Rest。
4. 数组操作革新
4.1 扩展运算符:数组操作的瑞士军刀
扩展运算符(Spread Operator)是ES6中最重要的新特性之一,它提供了一种简洁高效的方式来操作可迭代对象。
4.1.1 数组展开
javascript
// 【代码注释】展开运算符 (Spread):把数组/可迭代对象"摊开"成独立参数或元素
// 调用处 ...arr → 等价于逐一传入 arr[0], arr[1], arr[2]...
// 函数参数展开
function multiply(a, b, c, d) {
return a * b * c * d;
}
const numbers = [2, 3, 4, 5];
// 【代码注释】...numbers 等价于 multiply(2, 3, 4, 5)
console.log(multiply(...numbers)); // 120
// 数组合并
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 【代码注释】多个展开可并排,还可夹杂字面量------顺序即插入位置
const merged = [...arr1, ...arr2, 7, 8]; // [1, 2, 3, 4, 5, 6, 7, 8]
// 数组复制(浅拷贝)
const original = [1, 2, 3];
// 【代码注释】展开到新 [] 即浅拷贝,基本值独立;嵌套对象仍共享引用
const copy = [...original];
console.log(copy === original); // false(不同引用,内容相同)
// 将伪数组转换为真数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
// 【代码注释】⚠️ 普通对象没有 Symbol.iterator,展开会抛出 TypeError
const realArray = [...arrayLike]; // TypeError: arrayLike is not iterable
// 【代码注释】DOM NodeList / HTMLCollection 实现了迭代器,可直接展开
const nodeList = document.querySelectorAll('div');
const elementsArray = [...nodeList]; // 成功转换为 Array,可用 map/filter
// 【代码注释】字符串也是可迭代对象,展开后按 Unicode 码点分割(正确处理 emoji)
const string = 'Hello';
const charArray = [...string]; // ['H', 'e', 'l', 'l', 'o']
// 【代码注释】实战:合并默认配置与用户配置(后者覆盖前者)
const defaultConfig = { timeout: 3000, retries: 3, debug: false };
const userConfig = { timeout: 5000, debug: true };
const config = { ...defaultConfig, ...userConfig };
// { timeout: 5000, retries: 3, debug: true } ← userConfig 的同名键覆盖 default
【代码注释】展开运算符要求目标实现 Symbol.iterator 接口;普通 {...} 对象不可迭代,但 {...obj} 对象展开(Object spread)属于不同语法,仅复制自有可枚举属性。两者形式相同、语义不同:[...iter] 迭代展开,{...obj} 属性展开。
4.1.2 数组收集
javascript
// 函数参数收集
function collect(first, second, ...rest) {
console.log('第一个参数:', first);
console.log('第二个参数:', second);
console.log('剩余参数:', rest);
}
collect(1, 2, 3, 4, 5, 6);
// 数组解构中的收集
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// 扁平化嵌套数组
const nested = [1, [2, [3, [4, 5]]]];
const flattened = nested.flat(Infinity);
console.log(flattened); // [1, 2, 3, 4, 5]
// 对象属性收集
const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
console.log(rest); // { c: 3, d: 4 }
【代码注释】函数 Rest 得到实参数组;解构 Rest 得到剩余元素/属性,二者语法相同、语义不同。
扩展运算符的应用场景:
#mermaid-svg-V1GvH40R5UdhS0Iy{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-V1GvH40R5UdhS0Iy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-V1GvH40R5UdhS0Iy .error-icon{fill:#552222;}#mermaid-svg-V1GvH40R5UdhS0Iy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-V1GvH40R5UdhS0Iy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-V1GvH40R5UdhS0Iy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-V1GvH40R5UdhS0Iy .marker.cross{stroke:#333333;}#mermaid-svg-V1GvH40R5UdhS0Iy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-V1GvH40R5UdhS0Iy p{margin:0;}#mermaid-svg-V1GvH40R5UdhS0Iy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster-label text{fill:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster-label span{color:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster-label span p{background-color:transparent;}#mermaid-svg-V1GvH40R5UdhS0Iy .label text,#mermaid-svg-V1GvH40R5UdhS0Iy span{fill:#333;color:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy .node rect,#mermaid-svg-V1GvH40R5UdhS0Iy .node circle,#mermaid-svg-V1GvH40R5UdhS0Iy .node ellipse,#mermaid-svg-V1GvH40R5UdhS0Iy .node polygon,#mermaid-svg-V1GvH40R5UdhS0Iy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-V1GvH40R5UdhS0Iy .rough-node .label text,#mermaid-svg-V1GvH40R5UdhS0Iy .node .label text,#mermaid-svg-V1GvH40R5UdhS0Iy .image-shape .label,#mermaid-svg-V1GvH40R5UdhS0Iy .icon-shape .label{text-anchor:middle;}#mermaid-svg-V1GvH40R5UdhS0Iy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-V1GvH40R5UdhS0Iy .rough-node .label,#mermaid-svg-V1GvH40R5UdhS0Iy .node .label,#mermaid-svg-V1GvH40R5UdhS0Iy .image-shape .label,#mermaid-svg-V1GvH40R5UdhS0Iy .icon-shape .label{text-align:center;}#mermaid-svg-V1GvH40R5UdhS0Iy .node.clickable{cursor:pointer;}#mermaid-svg-V1GvH40R5UdhS0Iy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-V1GvH40R5UdhS0Iy .arrowheadPath{fill:#333333;}#mermaid-svg-V1GvH40R5UdhS0Iy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-V1GvH40R5UdhS0Iy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-V1GvH40R5UdhS0Iy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V1GvH40R5UdhS0Iy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-V1GvH40R5UdhS0Iy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V1GvH40R5UdhS0Iy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster text{fill:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy .cluster span{color:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy 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-V1GvH40R5UdhS0Iy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-V1GvH40R5UdhS0Iy rect.text{fill:none;stroke-width:0;}#mermaid-svg-V1GvH40R5UdhS0Iy .icon-shape,#mermaid-svg-V1GvH40R5UdhS0Iy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V1GvH40R5UdhS0Iy .icon-shape p,#mermaid-svg-V1GvH40R5UdhS0Iy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-V1GvH40R5UdhS0Iy .icon-shape .label rect,#mermaid-svg-V1GvH40R5UdhS0Iy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V1GvH40R5UdhS0Iy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-V1GvH40R5UdhS0Iy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-V1GvH40R5UdhS0Iy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 扩展运算符应用
数组操作
函数调用
对象操作
字符串处理
数组合并
数组复制
数组扁平化
参数展开
Rest参数
对象合并
对象解构
字符串转数组
字符串分割
【代码注释】扩展运算符是「可迭代对象」的通用工具:数组拷贝/合并、函数传参展开、字符串转字符数组、对象浅合并(ES2018)均依赖同一语法。
可运行示例 · 扩展运算符展开数组(含 DOM):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>扩展运算符 - 展开</title>
</head>
<body>
<ul class="news">
<li>新闻 A</li><li>新闻 B</li><li>新闻 C</li>
</ul>
<script>
const nums = [1000, 2000, 3000, 4000];
function func(n1, n2, n3, n4) {
console.log(n1, n2, n3, n4);
}
func(...nums);
func(...['甲', '乙', '丙']);
const arr01 = [100, 200, 300];
const arr02 = [...arr01];
arr02[2] = '新值';
console.log('原数组未改:', arr01);
console.log('拷贝:', arr02);
const merged = [...nums, ...arr01, '追加'];
console.log('合并:', merged);
const lis = [...document.querySelectorAll('.news li')];
console.log('NodeList → 数组:', lis);
console.log([...'Hello']);
</script>
</body>
</html>
【代码注释】func(nums) 只传一个数组实参;func(...nums) 才拆成四个独立参数。浅拷贝嵌套对象时内层仍共享引用。
可运行示例 · 扩展运算符收集到数组:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>扩展运算符 - 收集</title>
</head>
<body>
<script>
function func(...args) {
console.log(args);
}
func(100, 200, 300, 400, 500);
const [a1, a2, ...rest] = ['甲', '乙', '丙', '丁', '戊'];
console.log(a1, a2, rest);
</script>
</body>
</html>
【代码注释】解构中的 Rest 必须放在最后;收集得到的是新数组,修改 rest 不影响原数组剩余部分(元素若为对象则仍可能共享引用)。
4.2 Array构造函数的增强
ES6为Array构造函数添加了新的静态方法,提供了更强大和灵活的数组创建能力。
javascript
// Array.of(): 创建包含任意参数的数组
console.log(Array.of(1)); // [1]
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(Array.of(undefined)); // [undefined]
// 与Array()构造函数的对比
console.log(Array(3)); // [empty × 3] (创建了长度为3的空数组)
console.log(Array.of(3)); // [3] (创建了包含元素3的数组)
// Array.from(): 从类数组对象创建数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const arr1 = Array.from(arrayLike); // ['a', 'b', 'c']
// 从可迭代对象创建数组
const set = new Set([1, 2, 3, 3, 2, 1]);
const arr2 = Array.from(set); // [1, 2, 3]
// 带映射函数的Array.from()
const arr3 = Array.from([1, 2, 3], x => x * x); // [1, 4, 9]
// 实际应用:DOM节点转换
const lis = document.querySelectorAll('li');
const lisArray = Array.from(lis);
const lisTexts = Array.from(lis, li => li.textContent);
// 生成数字序列
const range = Array.from({ length: 10 }, (_, i) => i + 1);
console.log(range); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 字符串处理
const str = 'hello world';
const chars = Array.from(str);
const upperChars = Array.from(str, char => char.toUpperCase());
【代码注释】Array.from()接受两个参数:第一个是要转换的类数组对象或可迭代对象,第二个是可选的映射函数。这提供了比扩展运算符更灵活的转换能力。
可运行示例 · Array.of 与 Array.from:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Array.of / Array.from</title>
</head>
<body>
<ul class="news"><li>a</li><li>b</li></ul>
<script>
console.log(Array.of(100, 200, 'x'));
console.log(Array.of(12));
console.log(Array(3), Array.of(3));
const lis = Array.from(document.querySelectorAll('.news li'));
console.log(Array.from('Hello'));
console.log(Array.from([1, 2, 3], x => x * x));
</script>
</body>
</html>
【代码注释】Array(3) 创建长度为 3 的空槽数组;Array.of(3) 创建单元素 [3]。Array.from 可处理没有 Symbol.iterator 的类数组(带 length)。
4.3 数组实例方法的新增特性
ES6及后续版本为数组实例添加了许多有用的新方法,大大提高了数组操作的便捷性和表达能力。
4.3.1 查找方法
javascript
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 25 }
];
// find(): 查找第一个满足条件的元素
const user1 = users.find(user => user.age > 25);
console.log(user1); // { id: 2, name: '李四', age: 30 }
// findIndex(): 查找第一个满足条件的元素索引
const index1 = users.findIndex(user => user.age === 25);
console.log(index1); // 0 (第一个年龄为25的用户索引)
// 与indexOf()的对比
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.indexOf(3)); // 2
console.log(numbers.findIndex(x => x === 3)); // 2
// 对于对象数组,findIndex更强大
const index2 = users.findIndex(user => user.name === '王五');
console.log(index2); // 2
// includes(): 检查数组是否包含特定元素
console.log(numbers.includes(3)); // true
console.log(numbers.includes(10)); // false
// includes支持NaN检测
console.log([NaN].indexOf(NaN)); // -1 (indexOf无法正确检测NaN)
console.log([NaN].includes(NaN)); // true (includes可以正确检测NaN)
// at(): 使用相对索引访问元素
const arr = [10, 20, 30, 40, 50];
console.log(arr.at(0)); // 10
console.log(arr.at(-1)); // 50 (最后一个元素)
console.log(arr.at(-2)); // 40 (倒数第二个元素)
【代码注释】find()和findIndex()接受回调函数,提供比indexOf()更灵活的查找能力。at()方法解决了使用负数索引访问数组末尾元素的问题。
4.3.2 遍历与转换方法
javascript
const data = [1, 2, 3, 4, 5];
// keys(): 返回索引的遍历器
for (const index of data.keys()) {
console.log(index); // 0, 1, 2, 3, 4
}
// values(): 返回元素值的遍历器
for (const value of data.values()) {
console.log(value); // 1, 2, 3, 4, 5
}
// entries(): 返回键值对的遍历器
for (const [index, value] of data.entries()) {
console.log(`${index}: ${value}`); // 0: 1, 1: 2, 2: 3, 3: 4, 4: 5
}
// flat(): 数组扁平化
const nested = [1, [2, [3, [4, 5]]]];
console.log(nested.flat()); // [1, 2, [3, [4, 5]]] (默认扁平1层)
console.log(nested.flat(2)); // [1, 2, 3, [4, 5]] (扁平2层)
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5] (完全扁平)
// flatMap(): 先map后扁平化
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'How', 'are', 'you']
// 实际应用:移除空元素
const withEmpty = [1, , 2, , 3];
const noEmpty = withEmpty.flat();
console.log(noEmpty); // [1, 2, 3]
// fill(): 填充数组元素
const empty = new Array(5);
empty.fill(0);
console.log(empty); // [0, 0, 0, 0, 0]
// 部分填充
const partial = [1, 2, 3, 4, 5];
partial.fill('*', 1, 3);
console.log(partial); // [1, '*', '*', 4, 5]
【代码注释】flatMap()方法在处理数据转换时非常有用,它避免了创建中间数组,提高了性能。fill()方法在数组初始化时特别有用。
4.3.3 ES2023 不可变数组方法
ES2023 引入了不修改原数组 的镜像方法组,完美契合 React / Vue 的「不可变更新」范式,无需再手写 [...arr] 后修改:
javascript
const arr = [3, 1, 4, 1, 5, 9, 2, 6];
// toSorted():sort() 的不可变版本,返回新数组
const sorted = arr.toSorted((a, b) => a - b);
console.log(sorted); // [1, 1, 2, 3, 4, 5, 6, 9]
console.log(arr); // [3, 1, 4, 1, 5, 9, 2, 6](原数组不变)
// toReversed():reverse() 的不可变版本
const reversed = arr.toReversed();
console.log(reversed); // [6, 2, 9, 5, 1, 4, 1, 3]
// toSpliced():splice() 的不可变版本(从索引 2 删 2 个,插入 99 和 100)
const spliced = arr.toSpliced(2, 2, 99, 100);
console.log(spliced); // [3, 1, 99, 100, 5, 9, 2, 6]
// with():替换指定索引元素的不可变版本(arr[index] = value 的不可变版)
const updated = arr.with(0, 999);
console.log(updated); // [999, 1, 4, 1, 5, 9, 2, 6]
console.log(arr[0]); // 3(原数组未变)
// findLast() / findLastIndex():从末尾开始查找(ES2023)
const nums = [1, 2, 3, 4, 3, 2, 1];
console.log(nums.findLast(x => x > 2)); // 3(从末尾找第一个 > 2 的)
console.log(nums.findLastIndex(x => x > 2)); // 4(其索引)
// 对比 find/findIndex 从头查找:
console.log(nums.find(x => x > 2)); // 3(索引 2)
console.log(nums.findIndex(x => x > 2)); // 2
React 状态更新对比:
javascript// 旧方案:展开数组后用 map 替换元素 setTodos(prev => prev.map((item, i) => i === idx ? { ...item, done: true } : item)); // ES2023 with():语义更清晰 setTodos(prev => prev.with(idx, { ...prev[idx], done: true })); // ES2023 toSorted():排序不影响原 state const sorted = todos.toSorted((a, b) => a.priority - b.priority);
浏览器兼容性 :Chrome 110+、Firefox 115+、Safari 16+,Node.js 20+。生产环境可配合 core-js polyfill。
4.4 数组操作的实际应用
javascript
// 1. 数据去重
const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4]
// 2. 数组排序与对象数组排序
const products = [
{ name: 'Laptop', price: 999 },
{ name: 'Phone', price: 699 },
{ name: 'Tablet', price: 299 }
];
products.sort((a, b) => a.price - b.price);
console.log(products);
// [
// { name: 'Tablet', price: 299 },
// { name: 'Phone', price: 699 },
// { name: 'Laptop', price: 999 }
// ]
// 3. 数据分组
const people = [
{ name: '张三', age: 25, dept: 'engineering' },
{ name: '李四', age: 30, dept: 'design' },
{ name: '王五', age: 25, dept: 'engineering' },
{ name: '赵六', age: 30, dept: 'design' }
];
// 【代码注释】ES5/ES6 传统写法:用 reduce 手动分组,需要初始化判断
const groupedByAge = people.reduce((groups, person) => {
const age = person.age;
if (!groups[age]) {
groups[age] = [];
}
groups[age].push(person);
return groups;
}, {});
console.log(groupedByAge);
// {
// 25: [{ name: '张三', ... }, { name: '王五', ... }],
// 30: [{ name: '李四', ... }, { name: '赵六', ... }]
// }
// 【代码注释】ES2024 Object.groupBy:原生分组,一行搞定,语义更清晰
// 回调返回值即为分组键(字符串/数字)
const byDept = Object.groupBy(people, person => person.dept);
console.log(byDept);
// {
// engineering: [{ name: '张三', ... }, { name: '王五', ... }],
// design: [{ name: '李四', ... }, { name: '赵六', ... }]
// }
// 【代码注释】按年龄段(区间)分组------回调可包含任意逻辑
const byAgeGroup = Object.groupBy(people, ({ age }) =>
age < 25 ? 'junior' : age < 35 ? 'mid' : 'senior'
);
console.log(byAgeGroup.mid); // 张三、李四、王五、赵六(25-34 岁)
// 【代码注释】Map.groupBy:与 Object.groupBy 相同用法,但键可以是任意类型(对象、Symbol...)
const byDeptMap = Map.groupBy(people, person => person.dept);
console.log(byDeptMap.get('engineering').length); // 2
// 兼容性备注:Object.groupBy / Map.groupBy 在 Chrome 117+、Node 21+ 可用
// 旧环境可用 lodash _.groupBy 或上面的 reduce 写法作为 polyfill
// 4. 数据转换与提取
const users = [
{ id: 1, name: '张三', profile: { age: 25, city: '北京' } },
{ id: 2, name: '李四', profile: { age: 30, city: '上海' } }
];
const userNames = users.map(user => user.name);
const userCities = users.map(user => user.profile.city);
// 5. 条件筛选与统计
const orders = [
{ product: 'A', quantity: 2, price: 100 },
{ product: 'B', quantity: 1, price: 200 },
{ product: 'A', quantity: 3, price: 100 }
];
const totalByProduct = orders.reduce((totals, order) => {
totals[order.product] = (totals[order.product] || 0) + order.quantity * order.price;
return totals;
}, {});
console.log(totalByProduct); // { A: 500, B: 200 }
【代码注释】reduce 一次遍历完成 map+filter 可减少中间数组;大数据量时注意回调内勿做重计算。
可运行示例 · 数组实例新方法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>数组实例新方法</title>
</head>
<body>
<script>
const users = [
{ name: '刘备', age: 15 }, { name: '关羽', age: 43 },
{ name: '张飞', age: 41 }, { name: '诸葛亮', age: 45 },
{ name: '曹操', age: 54 }
];
console.log(users.find(u => u.age >= 45));
console.log(users.findIndex(u => u.age >= 45));
const nums = [100, 200, 300, 400, 500];
console.log(nums.includes(300));
console.log(nums.at(-1), nums.at(-3));
console.log(nums[-1]);
const nested = [[1, [2, [3]]], 'x'];
console.log(nested.flat(), nested.flat(2), nested.flat(Infinity));
console.log(nested.flatMap(item => Array.isArray(item) ? item : item + 100));
</script>
</body>
</html>
【代码注释】flatMap 等价于 map 后 flat(1);fill 会修改原数组,常用于初始化定长缓冲区。
经典应用场景:
- 数据处理 :
flat(Infinity)扁平化树形 API 数据 - 状态管理 :
find在列表中定位实体 - 表单校验 :
includes检查选项是否合法
4.5 数组模块知识点归纳
| 方法 | 类型 | 记忆要点 |
|---|---|---|
find / findIndex |
访问器 | 回调条件,找不到返回 undefined / -1 |
findLast / findLastIndex |
访问器 | 从末尾查找(ES2023) |
includes |
访问器 | 可检测 NaN,第二参 fromIndex |
at |
访问器 | 支持负数索引 |
flat / flatMap |
访问器 | 默认深度 1 |
fill |
修改器 | 原地填充 |
keys / values / entries |
遍历器 | 配合 for...of |
Object.groupBy |
静态(ES2024) | 原生分组,替代 reduce 手写分组 |
Map.groupBy |
静态(ES2024) | 同上,但键可为任意类型 |
5. 对象系统现代化
5.1 对象字面量语法增强
ES6对对象字面量的语法进行了多项改进,使得对象的创建和操作更加简洁和直观。
5.1.1 属性简写
javascript
// 【代码注释】属性名与变量名相同时可省略 `: 变量名`,只写一次键名即可
const name = '张三';
const age = 25;
const city = '北京';
// ❌ ES5:键名与值重复书写,冗余
const userES5 = {
name: name,
age: age,
city: city
};
// ✅ ES6 属性简写:同名直接省略,更简洁
const userES6 = {
name, // 等价于 name: name
age, // 等价于 age: age
city // 等价于 city: city
};
// 【代码注释】实战:函数返回多个值时直接打包成对象
function getUserInfo() {
const id = 42;
const username = 'zhangsan';
const role = 'admin';
return { id, username, role }; // 简写,避免 id: id 的重复
}
// 【代码注释】ES5 方法写法:属性值是 function 表达式,冗长且 this 绑定到调用对象
const mathUtils = {
add: function(a, b) {
return a + b;
},
subtract: function(a, b) {
return a - b;
}
};
// 【代码注释】ES6 方法简写:省略 `: function`,更接近 class 写法;
// 与属性简写不同,方法简写会为函数创建 [[HomeObject]],支持 super 调用
const mathUtilsES6 = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
// 【代码注释】Generator 方法简写:在方法名前加 *
// 调用后返回迭代器,每次 .next() 执行到下一个 yield
*generateNumbers() {
yield 1; // 第一次 .next() 返回 { value: 1, done: false }
yield 2; // 第二次 .next() 返回 { value: 2, done: false }
yield 3; // 第三次 .next() 返回 { value: 3, done: false }
// 第四次 .next() 返回 { value: undefined, done: true }
}
};
// 【代码注释】遍历 generator 方法
const gen = mathUtilsES6.generateNumbers();
console.log([...gen]); // [1, 2, 3] --- 展开迭代器即可收集所有值
【代码注释】属性简写只在属性名与局部变量同名 时有效;方法简写去掉 : function,两者都让对象字面量更接近 JSON,减少视觉噪音。Generator 方法配合 for...of / 展开运算符可惰性产出值序列。
5.1.2 计算属性名
javascript
// 【代码注释】计算属性名:[表达式] 在字面量内直接求值为键名
// ES5 只能先创建对象再动态赋键;ES6 可在字面量中一步到位
const prop1 = 'firstName';
const prop2 = 'lastName';
const prop3 = 'age';
const user = {
[prop1]: '张', // 键名 = 'firstName'
[prop2]: '三', // 键名 = 'lastName'
[prop3]: 25, // 键名 = 'age'
[`full_${prop1}_${prop2}`]: '张三' // 模板字符串作为键名表达式
};
console.log(user.firstName); // '张'
console.log(user[`full_firstName_lastName`]); // '张三'
// 【代码注释】实战一:表单字段批量初始化(prefix + field 动态组合键名)
function createDynamicObject(prefix, fields) {
const obj = {};
fields.forEach(field => {
obj[`${prefix}_${field}`] = '';
});
return obj;
}
const config = createDynamicObject('user', ['name', 'email', 'age']);
console.log(config); // { user_name: '', user_email: '', user_age: '' }
// 【代码注释】实战二:状态管理 reducer --- 用计算属性名按 id 更新列表项
function updateItem(state, id, changes) {
return {
...state,
[id]: { ...state[id], ...changes } // 以 id 为键,合并变更
};
}
// 【代码注释】实战三:Symbol 作键名 --- 不会出现在 for...in / Object.keys() 中
// 常用于为第三方对象附加"隐藏"元数据,避免键名冲突
const ID_KEY = Symbol('id');
const META_KEY = Symbol('meta');
const data = {
[ID_KEY]: 12345,
[META_KEY]: { createdAt: '2024-01-01' },
name: '示例数据'
};
console.log(data[ID_KEY]); // 12345
console.log(Object.keys(data)); // ['name'] ← Symbol 键不在此列
console.log(Object.getOwnPropertySymbols(data)); // [Symbol(id), Symbol(meta)]
【代码注释】[expr] 在字面量中求值类型规则:若结果不是字符串,会调用 .toString();Symbol 例外,保持 Symbol 类型作键。计算属性名在 Redux/Zustand 的 reducer 中极为常见({ ...state, [action.id]: newValue })。
5.1.3 Getter 与 Setter(访问器属性)
访问器属性 通过 get/set 关键字定义。对外表现与普通属性完全相同(直接 . 访问),但在读取/赋值时会自动调用对应函数,可在内部添加验证、计算等逻辑:
javascript
const user = {
_name: '张三', // 约定下划线前缀表示「内部存储变量」
// getter:读取 user.name 时触发
get name() {
return this._name;
},
// setter:执行 user.name = x 时触发,可加入校验
set name(value) {
if (typeof value !== 'string' || !value.trim()) {
throw new TypeError('名字必须是非空字符串');
}
this._name = value.trim(); // 自动去除首尾空格
}
};
console.log(user.name); // '张三'(触发 getter)
user.name = ' 李四 '; // 触发 setter,自动 trim
console.log(user.name); // '李四'
// user.name = 123; // TypeError: 名字必须是非空字符串
// 只读计算属性:只有 getter,无 setter
const rect = {
width: 10,
height: 20,
get area() {
return this.width * this.height; // 面积
},
get perimeter() {
return 2 * (this.width + this.height); // 周长
}
};
console.log(rect.area); // 200
console.log(rect.perimeter); // 60
rect.area = 999; // 静默失败(严格模式下 TypeError)
// 实际应用:带缓存的懒计算(只在首次访问时计算)
const obj = {
_cache: null,
get expensiveValue() {
if (this._cache !== null) return this._cache;
// 模拟耗时计算
this._cache = Array.from({ length: 1000 }, (_, i) => i).reduce((a, b) => a + b);
return this._cache;
}
};
getter/setter 与 Object.defineProperty 的关系 :对象字面量中的 get/set 语法,等价于调用 Object.defineProperty(obj, 'key', { get: fn, set: fn }),本质相同,字面量语法更简洁。
经典应用场景:
- Vue 2 响应式原理 :底层用
Object.defineProperty的 getter/setter 追踪依赖与触发更新 - Vue 3 Computed :
computed()返回的 ref 内部就是 getter - 表单数据校验:setter 拦截并验证输入,保证数据合法性
- 只读属性 :只定义 getter,外部无法覆盖(如
rect.area)
5.2 对象方法的this与super关键字
ES6在对象方法中引入了super关键字,提供了更清晰的继承机制。
javascript
// super关键字的使用
const parent = {
greet() {
return '你好,我是父类';
},
introduce() {
return `父类介绍: ${this.name}`;
}
};
const child = {
name: '子类',
// 方法简写形式可以使用super
greet() {
// super指向当前对象的原型
return `${super.greet()},这是子类的扩展`;
},
introduce() {
return `${super.introduce()},扩展信息`;
},
// 箭头函数不能使用super
// arrowMethod: () => { super.greet(); } // 报错
// 传统函数也不能使用super
// traditionalMethod: function() { super.greet(); } // 报错
// 只能在方法简写形式中使用super
validSuper() {
console.log(super.greet());
}
};
Object.setPrototypeOf(child, parent);
console.log(child.greet()); // "你好,我是父类,这是子类的扩展"
// super的实际应用:混入模式
const Serializable = {
toJSON() {
const props = {};
for (const key of Object.keys(this)) {
if (typeof this[key] !== 'function') {
props[key] = this[key];
}
}
return JSON.stringify(props);
}
};
const user = {
name: '张三',
age: 25,
email: 'zhangsan@example.com',
toJSON() {
return `${super.toJSON ? '原始序列化:' : ''}${JSON.stringify({
name: this.name,
age: this.age
})}`;
}
};
Object.assign(user, Serializable);
【代码注释】super关键字只能在对象方法简写形式中使用,它指向当前对象的原型对象。这为方法继承和重写提供了更清晰的语法。
super关键字的指向机制:
#mermaid-svg-nt3v8du82zBzHG5g{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-nt3v8du82zBzHG5g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nt3v8du82zBzHG5g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nt3v8du82zBzHG5g .error-icon{fill:#552222;}#mermaid-svg-nt3v8du82zBzHG5g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nt3v8du82zBzHG5g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nt3v8du82zBzHG5g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nt3v8du82zBzHG5g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nt3v8du82zBzHG5g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nt3v8du82zBzHG5g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nt3v8du82zBzHG5g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nt3v8du82zBzHG5g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nt3v8du82zBzHG5g .marker.cross{stroke:#333333;}#mermaid-svg-nt3v8du82zBzHG5g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nt3v8du82zBzHG5g p{margin:0;}#mermaid-svg-nt3v8du82zBzHG5g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nt3v8du82zBzHG5g .cluster-label text{fill:#333;}#mermaid-svg-nt3v8du82zBzHG5g .cluster-label span{color:#333;}#mermaid-svg-nt3v8du82zBzHG5g .cluster-label span p{background-color:transparent;}#mermaid-svg-nt3v8du82zBzHG5g .label text,#mermaid-svg-nt3v8du82zBzHG5g span{fill:#333;color:#333;}#mermaid-svg-nt3v8du82zBzHG5g .node rect,#mermaid-svg-nt3v8du82zBzHG5g .node circle,#mermaid-svg-nt3v8du82zBzHG5g .node ellipse,#mermaid-svg-nt3v8du82zBzHG5g .node polygon,#mermaid-svg-nt3v8du82zBzHG5g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nt3v8du82zBzHG5g .rough-node .label text,#mermaid-svg-nt3v8du82zBzHG5g .node .label text,#mermaid-svg-nt3v8du82zBzHG5g .image-shape .label,#mermaid-svg-nt3v8du82zBzHG5g .icon-shape .label{text-anchor:middle;}#mermaid-svg-nt3v8du82zBzHG5g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nt3v8du82zBzHG5g .rough-node .label,#mermaid-svg-nt3v8du82zBzHG5g .node .label,#mermaid-svg-nt3v8du82zBzHG5g .image-shape .label,#mermaid-svg-nt3v8du82zBzHG5g .icon-shape .label{text-align:center;}#mermaid-svg-nt3v8du82zBzHG5g .node.clickable{cursor:pointer;}#mermaid-svg-nt3v8du82zBzHG5g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nt3v8du82zBzHG5g .arrowheadPath{fill:#333333;}#mermaid-svg-nt3v8du82zBzHG5g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nt3v8du82zBzHG5g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nt3v8du82zBzHG5g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nt3v8du82zBzHG5g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nt3v8du82zBzHG5g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nt3v8du82zBzHG5g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nt3v8du82zBzHG5g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nt3v8du82zBzHG5g .cluster text{fill:#333;}#mermaid-svg-nt3v8du82zBzHG5g .cluster span{color:#333;}#mermaid-svg-nt3v8du82zBzHG5g 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-nt3v8du82zBzHG5g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nt3v8du82zBzHG5g rect.text{fill:none;stroke-width:0;}#mermaid-svg-nt3v8du82zBzHG5g .icon-shape,#mermaid-svg-nt3v8du82zBzHG5g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nt3v8du82zBzHG5g .icon-shape p,#mermaid-svg-nt3v8du82zBzHG5g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nt3v8du82zBzHG5g .icon-shape .label rect,#mermaid-svg-nt3v8du82zBzHG5g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nt3v8du82zBzHG5g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nt3v8du82zBzHG5g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nt3v8du82zBzHG5g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 对象方法调用
super关键字
指向方法所在对象的原型
与调用者无关
与定义位置相关
只能在方法简写中使用
【代码注释】super 指向方法定义时所在对象的原型,与 this(指向调用者)解耦;仅能在 say() {} 简写中使用,箭头函数与传统 function 属性中均非法。
名词解析 · super :在 say() { super.xxx } 中,super 绑定到定义该方法的对象的原型 ,与谁调用 say 无关;this 则指向调用者。
可运行示例 · super 关键字:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>super 关键字</title>
</head>
<body>
<script>
const user01 = {
name: '小明',
say() {
console.log('this.name:', this.name);
console.log('super.name:', super.name);
}
};
user01.__proto__.name = '原型上的名字';
user01.say();
const user02 = [];
user02.name = '数组对象';
user02.__proto__.name = '数组原型名';
user02.say = user01.say;
user02.say();
const fn = user01.say;
fn();
</script>
</body>
</html>
【代码注释】fn() 单独调用时 this 为 window(严格模式为 undefined),但 super 仍指向 user01 的原型链------体现 super 与 this 解耦。
5.3 对象的扩展运算符
ES2018将扩展运算符引入对象系统,提供了与数组类似的操作能力。
5.3.1 对象展开
javascript
// 对象的浅拷贝
const original = {
name: '张三',
age: 25,
address: {
city: '北京',
district: '朝阳区'
}
};
const copy = { ...original };
console.log(copy === original); // false (不同的对象引用)
// 注意:扩展运算符是浅拷贝
copy.address.city = '上海';
console.log(original.address.city); // '上海' (原对象也被修改)
// 对象的合并
const baseConfig = {
host: 'localhost',
port: 3000,
timeout: 5000
};
const devConfig = {
port: 3001,
debug: true
};
const mergedConfig = { ...baseConfig, ...devConfig };
console.log(mergedConfig);
// { host: 'localhost', port: 3001, timeout: 5000, debug: true }
// 对象的展开与属性覆盖
const user = {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
};
const updateUser = {
...user,
age: 26,
phone: '13800138000'
};
console.log(updateUser);
// { name: '张三', age: 26, email: 'zhangsan@example.com', phone: '13800138000' }
// 选择性包含属性
const { name, ...partialUser } = user;
console.log(partialUser); // { age: 25, email: 'zhangsan@example.com' }
【代码注释】对象的扩展运算符执行的是浅拷贝,对于嵌套对象需要特别注意。对象的合并遵循"后面覆盖前面"的原则。
5.3.2 对象收集
javascript
// 对象解构中的rest属性
const user = {
name: '张三',
age: 25,
email: 'zhangsan@example.com',
phone: '13800138000',
address: '北京朝阳区'
};
const { name, email, ...rest } = user;
console.log(rest);
// { age: 25, phone: '13800138000', address: '北京朝阳区' }
// 函数参数中的rest属性
function processUser({ name, ...rest }) {
console.log(`处理用户: ${name}`);
return rest;
}
const userData = processUser(user);
console.log(userData);
// { age: 25, email: 'zhangsan@example.com', phone: '13800138000', address: '北京朝阳区' }
// 递归处理嵌套对象
function flattenObject(obj, prefix = '') {
const result = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
Object.assign(result, flattenObject(value, newKey));
} else {
result[newKey] = value;
}
}
return result;
}
const nested = {
user: {
name: '张三',
profile: {
age: 25,
address: {
city: '北京'
}
}
}
};
const flattened = flattenObject(nested);
console.log(flattened);
// { 'user.name': '张三', 'user.profile.age': 25, 'user.profile.address.city': '北京' }
【代码注释】对象 Rest 收集的是可枚举自有属性的剩余键;Symbol 键默认不包含在解构 Rest 中。
可运行示例 · 对象字面量新语法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>对象字面量新语法</title>
</head>
<body>
<script>
const username = '小明', age = 19, address = '上海';
const prop = 'homeAddress';
function say() { console.log('say'); }
const user = {
name: username, age, address,
[10 + 10]: '计算属性',
[prop]: '北京',
say,
getInfo() { console.log(this.age); }
};
console.log(user);
user.getInfo();
</script>
</body>
</html>
【代码注释】say 简写等价于 say: say;方法简写 getInfo(){} 的 this 指向调用该方法的实例。
可运行示例 · 对象扩展运算符:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>对象扩展运算符</title>
</head>
<body>
<script>
const user = { name: '小明', age: 45, address: '上海', say() {}, drink() {} };
const copy = { ...user };
copy.name = '副本';
console.log(user.name, copy.name);
const extra = { homeAddress: '北京', address: '纽约' };
const merged = { ...user, ...extra };
console.log(merged);
const { age, address, ...rest } = merged;
console.log(rest);
</script>
</body>
</html>
【代码注释】合并时同名属性以后者为准(address: '纽约')。Redux reducer 常用 { ...state, ...action.payload } 模式。
5.4 Object构造函数的新方法
ES6为Object构造函数添加了许多实用的新方法,增强了对象操作的能力。
5.4.1 对象比较与合并
javascript
// Object.is(): 严格的相等比较
console.log(Object.is(100, 100)); // true
console.log(Object.is('100', 100)); // false
console.log(Object.is(NaN, NaN)); // true (与===不同)
console.log(Object.is(+0, -0)); // false (与===不同)
console.log(+0 === -0); // true
// Object.assign(): 对象合并
const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const source2 = { c: 5, d: 6 };
const result = Object.assign(target, source1, source2);
console.log(result); // { a: 1, b: 3, c: 5, d: 6 }
console.log(target === result); // true (修改了目标对象)
// 深拷贝的Object.assign实现
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
// Object.assign的实际应用
const defaults = {
theme: 'light',
language: 'en',
pageSize: 10
};
const userConfig = {
language: 'zh',
pageSize: 20
};
const finalConfig = Object.assign({}, defaults, userConfig);
console.log(finalConfig); // { theme: 'light', language: 'zh', pageSize: 20 }
【代码注释】Object.is()提供了更严格的相等比较,解决了===运算符在处理NaN和+0/-0时的问题。Object.assign()会修改目标对象,需要注意。
5.4.2 对象遍历与转换
javascript
const user = {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
};
// Object.keys(): 获取所有键
const keys = Object.keys(user);
console.log(keys); // ['name', 'age', 'email']
// Object.values(): 获取所有值
const values = Object.values(user);
console.log(values); // ['张三', 25, 'zhangsan@example.com']
// Object.entries(): 获取键值对数组
const entries = Object.entries(user);
console.log(entries);
// [
// ['name', '张三'],
// ['age', 25],
// ['email', 'zhangsan@example.com']
// ]
// Object.fromEntries(): 从键值对数组创建对象
const fromEntries = Object.fromEntries(entries);
console.log(fromEntries); // { name: '张三', age: 25, email: 'zhangsan@example.com' }
// 实际应用:对象转换
const userMap = new Map(entries);
console.log(userMap.get('name')); // '张三'
const backToObject = Object.fromEntries(userMap);
console.log(backToObject); // 原始对象
// 数据过滤
const filteredEntries = Object.entries(user)
.filter(([key, value]) => typeof value === 'string');
const filteredUser = Object.fromEntries(filteredEntries);
console.log(filteredUser); // { name: '张三', email: 'zhangsan@example.com' }
// 对象映射
const mappedEntries = Object.entries(user)
.map(([key, value]) => [key.toUpperCase(), value]);
const mappedUser = Object.fromEntries(mappedEntries);
console.log(mappedUser); // { NAME: '张三', AGE: 25, EMAIL: 'zhangsan@example.com' }
【代码注释】Object.entries + filter + fromEntries 可在不引入 lodash 的情况下完成对象 map/filter。
5.4.3 原型操作
javascript
// Object.getPrototypeOf(): 获取对象原型
const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
// Object.setPrototypeOf(): 设置对象原型
const parent = {
parentMethod() {
return '父类方法';
}
};
const child = {
childMethod() {
return '子类方法';
}
};
Object.setPrototypeOf(child, parent);
console.log(child.parentMethod()); // '父类方法'
// Object.getOwnPropertyDescriptors(): 获取所有属性描述符
const descriptorObj = {
name: '张三',
get fullName() {
return `${this.name} 先生`;
},
set fullName(value) {
this.name = value;
}
};
const descriptors = Object.getOwnPropertyDescriptors(descriptorObj);
console.log(descriptors);
// {
// name: { value: '张三', writable: true, enumerable: true, configurable: true },
// fullName: { get: [Function: get fullName], set: [Function: set fullName], enumerable: true, configurable: true }
// }
// 完整的对象复制(包括getter/setter)
const perfectClone = Object.create(
Object.getPrototypeOf(descriptorObj),
Object.getOwnPropertyDescriptors(descriptorObj)
);
【代码注释】Object.getOwnPropertyDescriptors()提供了对象的完整属性描述信息,包括getter/setter方法,这对于实现精确的对象复制非常重要。
5.4.4 属性检查
javascript
// Object.hasOwn(): 检查属性是否为对象自身属性(ES2022)
const obj = {
ownProperty: '自身属性'
};
obj.inheritedProperty = '继承属性';
const proto = {
protoProperty: '原型属性'
};
Object.setPrototypeOf(obj, proto);
// Object.hasOwn() 替代 Object.prototype.hasOwnProperty()
console.log(Object.hasOwn(obj, 'ownProperty')); // true
console.log(Object.hasOwn(obj, 'protoProperty')); // false
// 与in运算符的对比
console.log('ownProperty' in obj); // true
console.log('protoProperty' in obj); // true (包含继承属性)
// 实际应用:安全的属性检查
function safelyGetOwnProperty(obj, prop) {
if (Object.hasOwn(obj, prop)) {
return obj[prop];
}
return undefined;
}
// 防止原型污染
function mergeObjects(target, source) {
for (const [key, value] of Object.entries(source)) {
if (Object.hasOwn(source, key)) {
target[key] = value;
}
}
return target;
}
【代码注释】Object.hasOwn 替代 obj.hasOwnProperty(prop),避免对象无原型或 hasOwnProperty 被覆盖的问题。
5.4.5 对象冻结、密封与属性描述符
ES5+ 提供了三个级别的对象可变性控制,在 Redux、不可变数据、配置保护等场景中频繁使用:
javascript
// ─── Object.freeze:完全冻结(不可增、不可删、不可改) ───
const CONFIG = Object.freeze({
API_URL: 'https://api.example.com',
TIMEOUT: 5000
});
CONFIG.TIMEOUT = 9999; // 静默失败(严格模式抛 TypeError)
CONFIG.NEW_KEY = 'x'; // 静默失败
delete CONFIG.API_URL; // 静默失败
console.log(CONFIG.TIMEOUT); // 5000(未被修改)
// ⚠ freeze 只冻结第一层(浅冻结)
const state = Object.freeze({ user: { name: '张三' } });
state.user.name = '李四'; // ✅ 嵌套对象未被冻结!
console.log(state.user.name); // '李四'
// 深冻结工具函数(递归冻结所有层级)
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(key => {
const val = obj[key];
if (val && typeof val === 'object') deepFreeze(val);
});
return Object.freeze(obj);
}
// ─── Object.seal:密封(可修改现有属性,不可增删) ───
const point = Object.seal({ x: 1, y: 2 });
point.x = 100; // ✅ 允许修改
point.z = 3; // ❌ 静默失败(不可新增)
delete point.x; // ❌ 静默失败(不可删除)
console.log(point); // { x: 100, y: 2 }
// ─── Object.defineProperty:精细控制单个属性的描述符 ───
const VERSION_OBJ = {};
Object.defineProperty(VERSION_OBJ, 'VERSION', {
value: '1.0.0',
writable: false, // 不可修改值
enumerable: false, // 不出现在 for...in / Object.keys
configurable: false // 不可删除或重新定义
});
console.log(VERSION_OBJ.VERSION); // '1.0.0'
VERSION_OBJ.VERSION = '2.0'; // 静默失败
console.log(Object.keys(VERSION_OBJ)); // [](不可枚举,不出现)
// Object.isFrozen / isSealed:检查状态
console.log(Object.isFrozen(CONFIG)); // true
console.log(Object.isSealed(point)); // true(freeze 的对象也是 sealed)
| 操作 | Object.freeze |
Object.seal |
默认对象 |
|---|---|---|---|
| 修改已有属性值 | ❌ | ✅ | ✅ |
| 新增属性 | ❌ | ❌ | ✅ |
| 删除属性 | ❌ | ❌ | ✅ |
| 修改属性描述符 | ❌ | ❌ | ✅ |
经典应用场景:
- Redux 开发模式 :
Object.freeze(state)保护 store,防止 reducer 意外直接修改 - 应用级配置常量 :冻结
window.APP_CONFIG,防止第三方脚本篡改 - Vue 2 性能优化 :大型只读列表传入
Object.freeze(list)可跳过响应式劫持,显著提升性能 - ES Module 命名导出 :模块顶层导出对象天然接近
sealed语义
可运行示例 · Object 静态方法:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Object 静态方法</title>
</head>
<body>
<script>
console.log(Object.is(100, '100'), Object.is(NaN, NaN), Object.is(+0, -0));
const user = { name: '小明', age: 45, say() {} };
console.log(Object.keys(user), Object.values(user));
const entries = Object.entries(user);
console.log(Object.fromEntries(entries));
console.log(Object.hasOwn(user, 'age'), Object.hasOwn(user, 'toString'));
</script>
</body>
</html>
【代码注释】Object.is(NaN, NaN) 为 true;+0 === -0 为 true 但 Object.is(+0, -0) 为 false。
可运行示例 · Object.assign 合并对象:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>合并对象</title>
</head>
<body>
<script>
const user = { name: '小明', age: 45, address: '上海' };
const data = { homeAddress: '伦敦', address: '北京' };
const nums = [10, 20, 30];
const merged = Object.assign({}, user, data, nums);
console.log(merged);
const copy = Object.assign({}, user);
copy.name = '副本';
console.log(user.name, copy.name);
</script>
</body>
</html>
【代码注释】Object.assign 会把数组索引当作键 '0','1'... 合并进对象;数组合并更宜用扩展运算符。
5.5 对象模块知识点归纳
| API | 用途 | 业界场景 |
|---|---|---|
| 属性/方法简写 | 减少重复键名 | 配置对象、DTO |
[expr] 计算属性 |
动态键 | 表单字段映射、Redux reducer |
super |
访问原型方法 | 混入(mixin) |
{ ...obj } |
浅拷贝/合并 | Redux、React setState |
Object.assign |
多源合并到目标 | 默认配置覆盖 |
Object.entries + fromEntries |
对象 ↔ 二维数组 | Map 互转、URLSearchParams |
Object.hasOwn |
自有属性检测 | 安全遍历、防原型污染 |
structuredClone() |
原生深拷贝(ES2022) | 克隆 Date/Map/Set,替代 JSON 序列化方案 |
6. 最佳实践与性能优化
6.1 数值处理最佳实践
javascript
// 1. 数值比较的正确方式
function numberEquals(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(numberEquals(0.1 + 0.2, 0.3)); // true
// 2. 大整数处理
function addBigNumbers(a, b) {
if (typeof a === 'bigint' || typeof b === 'bigint') {
return BigInt(a) + BigInt(b);
}
return a + b;
}
// 3. 数值格式化
const formatNumber = (num) => {
return new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(num);
};
console.log(formatNumber(1234.5)); // '1,234.50'
// 4. 数值验证
function isValidNumber(value) {
return typeof value === 'number' && !isNaN(value) && isFinite(value);
}
【代码注释】金额展示用 Intl.NumberFormat 而非手写千分位;超过安全整数范围务必转 BigInt 或字符串处理。
6.2 函数设计最佳实践
javascript
// 1. 函数参数默认值设计
function createUser({
name = '匿名用户',
age = 18,
email = '',
role = 'user'
} = {}) {
return { name, age, email, role };
}
// 2. 防御性编程
const safeFunction = (callback, ...args) => {
try {
if (typeof callback === 'function') {
return callback(...args);
}
} catch (error) {
console.error('函数执行出错:', error);
return null;
}
};
// 3. 函数组合
const compose = (...functions) => (initialValue) =>
functions.reduceRight((value, func) => func(value), initialValue);
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const combinedFunction = compose(addOne, double, square);
console.log(combinedFunction(3)); // square(3) -> double(9) -> addOne(18) -> 19
// 4. 柯里化
const curry = (fn) => {
const curried = (...args) => {
if (args.length >= fn.length) {
return fn(...args);
}
return (...more) => curried(...args, ...more);
};
return curried;
};
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
【代码注释】默认参数解构 ({ name = 'x' } = {}) 可防止未传参时报错;柯里化与 Rest/扩展配合可构建灵活的函数式 API。
6.3 数组操作性能优化
javascript
// 1. 大数组处理优化
function processLargeArray(array, processor, batchSize = 1000) {
const result = [];
for (let i = 0; i < array.length; i += batchSize) {
const batch = array.slice(i, i + batchSize);
result.push(...batch.map(processor));
// 给事件循环机会处理其他任务
if (i % (batchSize * 10) === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
return result;
}
// 2. 避免不必要的数组创建
// 不好
const doubled = numbers.map(x => x * 2).filter(x => x > 10);
// 好
const doubledOptimized = numbers.reduce((acc, x) => {
const doubled = x * 2;
if (doubled > 10) acc.push(doubled);
return acc;
}, []);
// 3. 选择合适的数组方法
// 查找元素:使用find而不是filter
const user = users.find(u => u.id === 1); // 好
// const user = users.filter(u => u.id === 1)[0]; // 不好
// 4. 避免在循环中修改数组长度
const items = [1, 2, 3, 4, 5];
for (let i = items.length - 1; i >= 0; i--) {
if (items[i] % 2 === 0) {
items.splice(i, 1);
}
}
【代码注释】链式 map().filter() 会生成中间数组;单次 reduce 更省内存。查找用 find 而非 filter()[0],找到即停止。
6.4 对象操作优化技巧
javascript
// 1. 对象属性的动态访问
const getValue = (obj, path) => {
return path.split('.').reduce((current, key) => current?.[key], obj);
};
const user = {
profile: {
address: {
city: '北京'
}
}
};
console.log(getValue(user, 'profile.address.city')); // '北京'
console.log(getValue(user, 'profile.phone')); // undefined
// 2. 对象深拷贝的高效实现(手动递归,兼容所有环境)
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
// 【代码注释】hash (WeakMap) 用于检测循环引用:若同一对象已被克隆过,直接返回克隆结果
if (hash.has(obj)) return hash.get(obj);
const clone = Array.isArray(obj) ? [] : {};
hash.set(obj, clone); // 先注册再递归,防止环状结构死循环
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
// 【代码注释】2b. structuredClone():原生深克隆(Node 17+ / 现代浏览器,无需手写)
// 相比手写 deepClone:① 原生 C++ 实现,速度更快;② 正确处理更多内置类型
const original = {
name: '张三',
birthday: new Date('2000-01-01'), // Date 对象 → 深克隆后仍是 Date
scores: [98, 87, 95],
address: { city: '北京', district: '朝阳区' }
};
const cloned = structuredClone(original);
// 验证:修改克隆不影响原对象
cloned.address.city = '上海';
cloned.birthday.setFullYear(1999);
console.log(original.address.city); // '北京'(互不影响)
console.log(original.birthday.getFullYear()); // 2000(互不影响)
// 【代码注释】structuredClone 支持的类型(JSON.stringify 不支持但 structuredClone 支持):
// Date / RegExp / Map / Set / ArrayBuffer / TypedArray / Error / BigInt
// ⚠️ 不支持:Function、DOM 节点、Symbol 键(会被忽略)、原型链(只克隆自有属性)
// 与 JSON.parse(JSON.stringify(obj)) 的对比
const withDate = { d: new Date() };
const jsonClone = JSON.parse(JSON.stringify(withDate));
console.log(typeof jsonClone.d); // 'string'(Date → string,精度丢失)
const structClone = structuredClone(withDate);
console.log(structClone.d instanceof Date); // true(Date 保持 Date 类型)
// 【代码注释】各深克隆方案对比
// | 方案 | Date | RegExp | Map/Set | 循环引用 | 速度 |
// |------------------------|------|--------|---------|---------|------|
// | JSON.parse/stringify | ❌ | ❌ | ❌ | ❌ | 一般 |
// | 手写递归 deepClone | ❌ | ❌ | ❌ | ✅ | 一般 |
// | structuredClone() | ✅ | ✅ | ✅ | ✅ | 最快 |
// | lodash cloneDeep | ✅ | ✅ | ✅ | ✅ | 较快 |
// 3. 对象的响应式更新
function createReactive(obj, callback) {
return new Proxy(obj, {
set(target, property, value) {
const oldValue = target[property];
target[property] = value;
callback(property, value, oldValue);
return true;
}
});
}
const reactiveUser = createReactive(user, (prop, newValue, oldValue) => {
console.log(`${prop} 从 ${oldValue} 变为 ${newValue}`);
});
reactiveUser.name = '李四'; // 触发回调
【代码注释】Proxy 是 ES6 元编程能力,Vue 3 响应式与本文对象展开互补:展开做快照合并,Proxy 做依赖追踪。
7. 总结与展望
ES6+为JavaScript带来了革命性的变化,不仅提高了开发效率,更重要的是为构建大型应用程序提供了坚实的语言基础。本文深入探讨了数值处理、函数增强、数组革新和对象现代化等核心特性,这些特性已经成为现代JavaScript开发的标准实践。
7.1 核心特性回顾
- 数值系统:新的表示法、BigInt类型、数字分隔符
- 函数增强:箭头函数、Rest参数、默认参数、标签模板
- 数组革新:扩展运算符、新的遍历方法、数组扁平化
- 对象现代化:属性简写、扩展运算符、新的Object方法
7.2 发展趋势
随着ECMAScript标准的持续发展,我们可以期待:
- 更好的类型支持:继续改进数值精度和类型系统
- 更强大的集合操作:更丰富的数据结构和算法
- 更优雅的语法:持续提升代码的可读性和表达力
- 更好的性能:优化现有特性的执行效率
7.3 学习建议
- 渐进式学习:先掌握核心特性,再深入高级用法
- 实际应用:在项目中实践,理解最佳实践
- 持续关注:跟踪ECMAScript标准的最新发展
- 性能意识:了解特性背后的性能影响
ES6+的掌握是每个JavaScript开发者的必经之路,希望本文能为你的学习之旅提供有价值的指导。
附录 A:特性与业界应用对照表
| ES6+ 特性 | 典型框架/库 | 具体用法 |
|---|---|---|
| 箭头函数 | React、Vue | 事件处理、useEffect 回调 |
| Rest / 扩展 | Redux、Immer | ...state 不可变更新 |
Array.from |
各种 DOM 库 | NodeList → 数组后 map |
find / includes |
业务代码 | 替代 lodash find / includes |
| 对象展开 | React setState、Vue Pinia |
浅合并 patch |
Object.entries |
URLSearchParams、表单 |
对象与键值对列表互转 |
| BigInt | 数据库驱动、链上应用 | 大 ID、精确整数 |
| 标签模板 | styled-components、lit | CSS / HTML 安全 DSL |
structuredClone() |
状态管理、测试 | 深克隆 store 快照、避免 JSON 序列化副作用 |
Object.groupBy |
数据展示层 | 替代 reduce 分组,报表/列表分类渲染 |
计算属性名 [key] |
Redux reducer | { ...state, [action.id]: newValue } 不可变更新 |
附录 B:官方与延伸阅读
| 资源 | 链接 | 说明 |
|---|---|---|
| MDN · 展开语法 | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax | 展开与 Rest 权威说明 |
| MDN · 箭头函数 | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions | this 词法绑定 |
| MDN · Rest 参数 | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/rest_parameters | 与 arguments 对比 |
| MDN · BigInt | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt | ES2020 大整数 |
| ECMA-262 | https://tc39.es/ecma262/ | 语言规范全文 |
| Exploring ES6 | Dr. Axel Rauschmayer | 特性动机与细节(书籍) |
本文涵盖 ES2015--ES2024 与 Day02 相关的数值、函数、数组、对象特性,包括 structuredClone()(ES2022)、Object.groupBy / Map.groupBy(ES2024)等新增内容;文中 HTML 示例可直接保存后在现代浏览器中打开,在控制台查看输出。