Day02_ES6+ 核心特性深度解析:现代 JavaScript 开发的基石

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, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#039;');
        }
        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>&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;</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 学习建议

  1. 渐进式学习:先掌握核心特性,再深入高级用法
  2. 实际应用:在项目中实践,理解最佳实践
  3. 持续关注:跟踪ECMAScript标准的最新发展
  4. 性能意识:了解特性背后的性能影响

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 示例可直接保存后在现代浏览器中打开,在控制台查看输出。

相关推荐
计算机魔术师1 小时前
管谷歌的不是Pichai一个人,是一整层AI智能体
前端
米米商聊1 小时前
图标能点,按钮就可用了吗?键盘、名称与收藏状态
前端·javascript
Younglina2 小时前
三个晚上,我和 WorkBuddy 做了个生图网站
前端·后端·ai编程
xy34532 小时前
Axure 9.0 内联框架加载方式
前端·ui·html·axure·原型·产品设计
技术小甜甜2 小时前
【Cursor】Skill 被标黄色警告,怎么改成合格的 SKILL.md
开发语言·javascript·ai编程·cursor·1024程序员节·ai agent·skills
陈随易3 小时前
副业搞钱点子之收入查看器
前端·后端
Profile排查笔记3 小时前
浏览器指纹逆向怎么分析:从网络请求追踪到 JavaScript 采集链
前端·人工智能·后端·自动化
hudie7653 小时前
LikeShop 二开最常改的 5 个文件(附改前改后对比)
前端·uni-app·php·个人开发
qetfw4 小时前
Bash 函数与流程控制:read、if、for、case 验证
linux·前端·javascript·bash
鬼手点金4 小时前
从 API 消息到模型 Token:Chat Template
java·前端·javascript·数据库·人工智能·深度学习·1024程序员节