JS随笔ES6特性与模块化实践本篇是「JS随笔」系列中的 ES6 特性与模块化篇从历史背景与版本演进切入系统梳理 ES6 及后续版本ES6的重要语言特性与模块化实践覆盖let/const、箭头函数、默认参数与模板字符串、展开与解构、类与继承、模块的导入导出、集合类型等。历史背景ES6的历史背景可以追溯到1996年当时 JS 的创造者Netscape公司决定将 JS 提交给国际标准化组织ECMA。次年ECMA发布了 262 号标准文件ECMA-262的第一版规定了浏览器脚本语言的标准并将这种语言称为ECMAScript。1997 至 1999 年ECMAScript 经历了2.0和3.0两个版本迭代其中 3.0 在业界广泛支持奠定了语言基本语法。2000年开始酝酿的ECMAScript 4.0因过于激进未通过其大量内容被 ES6 继承。2008年 Harmony 代号启动ES5在2009年发布。2013年 ES6 草案冻结2015年正式通过被称为ES2015。此后规范按年度持续迭代。应用场景模块化编程使用 ES6 模块组织项目结构与依赖。异步编程Promise、async/await简化异步控制流与错误处理。面向对象class与extends提供更清晰的封装与继承。函数式编程箭头函数与数组高阶函数map、filter、reduce。语法简化模板字符串、参数解构、展开运算符等。版本变动概览自 ES6ES2015起每年发布新版本加入新特性ES7 (2016)Array.prototype.includes、指数运算符**ES8 (2017)async/await、Object.values、Object.entries、字符串填充ES9 (2018)异步迭代器、for-await-of、Promise.finally、正则命名捕获ES10 (2019)Array.prototype.flat/flatMap、Object.fromEntries、字符串前后裁剪ES11 (2020)可选链?.、空值合并??ES12 (2021)逻辑赋值|| / / ??、Promise.allSettled年度更新ES2025/ES2026Iterator HelpersES2025迭代器原生支持.map()、.filter()、.take()等惰性管道方法适合数据流与大集合处理。[TC39/ES2025、Saeloun]Set 扩展ES2025集合数学运算union、intersection、difference、symmetricDifference以及关系判定isSubsetOf、isSupersetOf、isDisjointFrom。[InfoWorld、Saeloun]JSON 模块ES2025允许直接import data from ./data.json减少构建与手动解析。[InfoWorld]TemporalES2026 预计现代日期时间 API替代历史Date的精度与时区问题。[The New Stack / TC39 草案]Intl 与模块加载优化ES2026 预计如Intl.Locale变体与import defer改进国际化与加载性能。[The New Stack]新的变量声明方式let块级作用域值可重新赋值同一作用域不可重复声明存在暂时性死区let age; age 30; console.log(age); // 30const块级作用域常量声明时必须初始化引用不可重新绑定基本类型值不可变对象/数组引用不可变但内容可变const age 30; const obj { key: value }; obj.key newValue;与var的对比var具函数/全局作用域可重复声明存在变量提升与全局污染风险let/const避免上述问题推荐优先使用。实战中比较常见的一条经验是默认使用const仅在确实需要重新赋值时改用let避免在同一作用域混用多个声明同名变量以减少“阴影变量”导致的逻辑混淆箭头函数更简洁的函数定义单表达式可隐式返回无this、无arguments不适作构造函数call/apply/bind无效const multiply (x, y) x * y; const double x x * 2;默认参数与模板字符串默认参数允许在函数声明时为形参提供“兜底值”避免在函数体内部编写大量name name || xxx之类的兼容代码。 需要注意的是默认值只在实参为undefined时才会生效传入null会被视作“有效值”。模板字符串则通过反引号与${}插值语法将字符串拼接与表达式求值结合起来 非常适合用于构建多行文本、日志、SQL/GraphQL 片段等结构化内容。function greet(name World) { console.log(Hello, ${name}!); } greet();展开运算符与解构赋值展开运算符用于在“期望一串元素或键值对”的地方铺开数组或对象 是构建新数组/对象和实现浅拷贝的利器解构赋值则提供了从复杂结构中按模式提取字段的语法糖 大幅减少中间变量与重复访问。需要牢记两点展开/解构都是浅层操作嵌套对象仍然是共享引用解构时可以配合默认值与重命名提升代码的自文档性const arr [1, 2, 3]; const [a, ...b] arr; // a1, b[2,3] const obj { x: 1, y: 2, z: 3 }; const { x, ...y } obj; // x1, y{y:2, z:3}类class声明与静态方法class Person { constructor(name, age) { this.name name; this.age age; } greet() { console.log(Hello, my name is ${this.name}!); } static info() { console.log(This is a static method.); } } Person.info();继承与 superclass Employee extends Person { constructor(name, age, jobTitle) { super(name, age); this.jobTitle jobTitle; } jobDescription() { Person.info(); console.log(${this.name} is a ${this.jobTitle}.); } }getter/setter 与私有class PersonX { get age() { return this._age; } set age(v) { if (v 0) this._age v; } }ES6 本身无原生私有字段可通过Symbol或闭包模拟后续版本已标准化类私有字段以#形式与静态初始化等能力属于 ESNext/后续快照。模块化导出export function add(x, y) { return x y; } export const PI 3.14; const tools {}; export default tools;导入import { add, PI } from ./math.js; import defaultFunction from ./utils.js; import * as MathUtils from ./math.js; MathUtils.add(2, 3); if (someCondition) { import(./module.js) .then(m m.doSomething()) .catch(err console.error(err)); }模块化的好处封装性避免全局命名污染可维护性结构清晰、依赖明确可重用性跨项目复用依赖管理模块间边界与关系清晰迭代器与生成器迭代协议实现[Symbol.iterator]返回迭代器对象含next()生成器function*定义可暂停函数配合yield逐步产生值function* gen() { yield 1; yield 2; yield 3; } [...gen()]; // [1,2,3]Proxy 与 ReflectProxy为对象提供拦截器定义访问/赋值/函数调用等行为Reflect与Proxy配套的基本操作集合提升一致性与可预测性const target { x: 1 }; const p new Proxy(target, { get(obj, key) { return key in obj ? obj[key] : undefined; } }); p.x; // 1类私有字段与静态块ESNext私有字段使用#name声明外部不可访问静态初始化块在类加载时执行一次的初始化逻辑class Counter { #value 0; inc() { this.#value; } get value() { return this.#value; } static { // 静态初始化 } }顶层 await 与 Import Assertions顶层 await在模块顶层使用await简化初始化流程仅模块可用Import Assertions为导入资源提供类型断言如assert { type: json }const data await fetch(/config.json).then(r r.json()); import config from ./config.json assert { type: json };异步与 Promise简述const myPromise new Promise((resolve, reject) { // ... resolve(value); }); myPromise.then(v { /* ... */ }).catch(err { /* ... */ });链式调用fetch(https://api.example.com/data) .then(r r.json()) .then(data { /* ... */ }) .catch(err { /* ... */ });静态方法Promise.all([p1, p2, p3]).then(results { /* ... */ }); Promise.race([p1, p2]).then(result { /* ... */ });async/awaitasync function fetchData() { try { const response await fetch(https://api.example.com/data); const data await response.json(); } catch (error) { // ... } }Map / WeakMapconst map new Map(); map.set(key, value); map.get(key); map.has(key); map.delete(key); map.forEach((value, key) { /* ... */ });WeakMap键必须是对象弱引用不可遍历无size。Set / WeakSetconst mySet new Set(); mySet.add(1); mySet.add(text); mySet.add({ name: Tom }); mySet.has(1); mySet.size; mySet.delete(text); mySet.forEach(v console.log(v)); const uniqueNumbers [...new Set([1,2,2,3,4,4,5])];WeakSet仅存对象引用弱引用不可遍历。Symbol 与私有属性Symbol提供了一种“永不冲突”的键类型即便是描述相同的Symbol(desc)也彼此不相等。 这非常适合作为库或框架内部的扩展点键名避免与业务代码在对象属性上踩踏。在“私有属性”场景中Symbol更像是一种“弱隐藏”机制外部若持有该 Symbol 仍然可以访问对应属性 但在常规枚举如Object.keys/for...in中不会被轻易遍历出来。对于真正需要强封装的类内部状态ES 提供了#private字段语法 而 Symbol 更适合用在元数据、扩展协议、跨模块通信这些场景中与公开 API 共存。const mySymbol Symbol(mySymbol); const obj { [mySymbol]: Only one key };ES2025/ES2026 详解与示例Iterator HelpersES2025在迭代器上直接使用.map/.filter/.take/.drop/.flatMap等方法惰性处理适合大集合与流式数据const it [1,2,3,4,5].values(); const pipeline it.filter(x x % 2).map(x x * 10).take(3); for (const v of pipeline) { /* 1-10, 3-30, 5-50 */ }Set 扩展ES2025集合运算union/intersection/difference/symmetricDifference关系判断isSubsetOf/isSupersetOf/isDisjointFromconst A new Set([1,2,3]); const B new Set([3,4,5]); A.union(B); // Set {1,2,3,4,5} A.intersection(B); // Set {3} A.difference(B); // Set {1,2} A.symmetricDifference(B); // Set {1,2,4,5} A.isSubsetOf(new Set([1,2,3,4])); // trueJSON 模块ES2025直接导入 JSON 作为模块减少手动解析与构建步骤可配合 Import Assertions 明确类型import config from ./config.json assert { type: json }; console.log(config.title);TemporalES2026 预计现代日期时间 API精确、时区安全与模块初始化/调度结合避免Date的历史坑const now Temporal.Now.zonedDateTimeISO(Asia/Shanghai); const deadline now.add({ hours: 1 });import deferES2026 预计延迟部分导入的求值用于非关键模块的初始化import defer analytics from ./analytics.js; // 完成主业务初始化后再进行分析模块启动 queueMicrotask(() analytics.start());实践建议ES6 到 ES2026优先模块化与顶层 await简化初始化流程在数据密集场景使用 Iterator Helpers避免中间数组使用 Set 扩展实现集合运算替换手写版本以提高可读性通过 JSON 模块直接消费配置与数据快照引入 Temporal 做时间计算与时区管理针对非关键路径采用 import defer 与空闲调度策略生成器与异步迭代进阶将生成器与for...of结合打造数据管道异步迭代器支持for await...of逐块拉取数据function* range(n) { for (let i 0; i n; i) yield i; } const squares (function* (iter) { for (const x of iter) yield x * x; })(range(5)); [...squares]; // [0,1,4,9,16]async function* fetchPages(ids) { for (const id of ids) { const res await fetch(/api/${id}); yield res.json(); } }Proxy/Reflect 高级用法数据校验与只读视图变更跟踪与调试const model { x: 1 }; const ro new Proxy(model, { set() { throw new Error(read-only); }, get(obj, key) { return Reflect.get(obj, key); } });模块模式与实践入口index统一导出子模块动态导入配合路由与懒加载// src/index.js export * from ./math.js; export * from ./string.js;// 动态导入 const route settings; const page await import(./pages/${route}.js); page.render();类装配与组合技巧通过静态工厂封装构造复杂对象组合优于继承使用委托与小对象拼装class HttpClient { static fromToken(token) { return new HttpClient({ headers: { Authorization: Bearer ${token} } }); } constructor(opts) { this.opts opts; } get(url) { return fetch(url, this.opts); } }