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、扩展运算符、默认参数组合使用
模板字符串 ````` 与 ${} 插值 标签模板在其之上增加自定义处理函数
字符串新方法 includespadStart 与数组 includesat 形成对称 API 设计

0.3 核心名词百科

名词 英文 解析
字面量 Literal 在源码中直接写出的固定值,如 0b101045n
安全整数 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 3setupref/reactive 配合解构与 Rest 处理 props。
  • lodash 替代 :原生 findincludesflat 减少第三方依赖。
  • Node.jsfs.promisesimport() 链式调用依赖箭头函数保持 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。三者 binaryoctaldecimal 在此例中数值相等,便于理解「同一数值、多种字面量」。

名词解析 · 进制字面量0b 表示 binary(二进制),0o 表示 octal(八进制,字母 o 避免与数字 0 混淆),0x 表示 hexadecimal(十六进制)。严格模式下旧写法 010 可能被禁用,应优先使用 0o 前缀。

经典应用场景

  • 网络编程 :处理 IP 地址和子网掩码(如 0b11111111 表示 255)
  • 图形处理 :颜色通道位运算(如 #FF0xFF
  • 权限位掩码:用二进制位表示开关组合

可运行示例 · 八进制与二进制表示法 (保存为 .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 规定的「可接受浮点误差」单位:比较 ab 是否「近似相等」应写 Math.abs(a - b) < Number.EPSILON,而不是 a === bMAX_SAFE_INTEGER 之外的大整数会丢失精度(如 9007199254740992 === 9007199254740992 + 1true),此时应改用 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 系列遵循「无隐式类型转换」原则:参数必须先已是 numberisFinite/isInteger/isSafeInteger)或严格等于 NaNisNaN),避免全局 isNaN/isFinite'abc' 转成 NaN 再判断的陷阱。Number.parseIntparseInt 是同一函数,迁移时只需改调用路径。表单校验推荐: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.10.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,而用 number9007199254740991 + 1 可能已不精确。混用 100n + 100TypeError;比较时用 ===56n === 56 为 false)。JSON 默认不支持 BigInt 序列化,需自定义 toJSON 或转字符串。

名词解析 · BigInt :ES2020 新增的原始类型 ,字面量后缀 ntypeof 返回 '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 === 56false(类型不同);== 会做类型转换可能为 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.parseNumber('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/reducearguments 是类数组需转换,且包含全部实参而非「剩余」部分。新代码应优先 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 的函数Generatoraverage: () =>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 3computed(() => ...)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 比较函数若用箭头函数,无法访问 sortthis 参数(一般不需要);比较时建议 (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 等价于 mapflat(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 Computedcomputed() 返回的 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() 单独调用时 thiswindow(严格模式为 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 === -0trueObject.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 示例可直接保存后在现代浏览器中打开,在控制台查看输出。

相关推荐
问心无愧05131 小时前
ctf show web入门71
android·前端·笔记
light blue bird1 小时前
支组汇总主子节点工序路径图表
前端·jvm·.net·桌面端·gdi绘图
小KK_2 小时前
新手必看篇——JS类型判断
前端·javascript
小妖6662 小时前
console.log 显示内容不全怎么办
javascript·js·console.log
小小高不懂写代码2 小时前
Vibe Coding时代的自我鞭策
前端·人工智能
喵个咪2 小时前
基于 Nuxt 4 的现代 Headless CMS 前端:架构深度解析与二次开发指南
前端·vue.js·nuxt.js
AI科技星2 小时前
万有引力G与真空介电常数ε0全维度完整关系式汇编(基于v=c螺旋时空理论)
c语言·开发语言·前端·javascript·网络·汇编·electron
didadida2622 小时前
第二回: Session Assistant 工具链的三节点设计
javascript·agent
云间寄信2 小时前
异步编程与事件循环
javascript