JS随笔:ES6+特性与模块化实践

JS随笔:ES6+特性与模块化实践

本篇是「JS随笔」系列中的 ES6+ 特性与模块化篇,从历史背景与版本演进切入,系统梳理 ES6 及后续版本(ES6+)的重要语言特性与模块化实践,覆盖 let/const、箭头函数、默认参数与模板字符串、展开与解构、类与继承、模块的导入导出、集合类型等。


原文地址

墨渊书肆/JS随笔:ES6+特性与模块化实践


历史背景

ES6 的历史背景可以追溯到 1996 年,当时 JS 的创造者 Netscape 公司决定将 JS 提交给国际标准化组织 ECMA。次年,ECMA 发布了 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript

1997 至 1999 年,ECMAScript 经历了 2.03.0 两个版本迭代,其中 3.0 在业界广泛支持,奠定了语言基本语法。2000 年开始酝酿的 ECMAScript 4.0 因过于激进未通过,其大量内容被 ES6 继承。2008 年 Harmony 代号启动,ES52009 年发布。2013 年 ES6 草案冻结,2015 年正式通过,被称为 ES2015。此后规范按年度持续迭代。

应用场景

  • 模块化编程:使用 ES6 模块组织项目结构与依赖。
  • 异步编程Promiseasync/await 简化异步控制流与错误处理。
  • 面向对象classextends 提供更清晰的封装与继承。
  • 函数式编程 :箭头函数与数组高阶函数(mapfilterreduce)。
  • 语法简化:模板字符串、参数解构、展开运算符等。

版本变动(概览)

自 ES6(ES2015)起每年发布新版本,加入新特性:

  1. ES7 (2016)Array.prototype.includes、指数运算符 **
  2. ES8 (2017)async/awaitObject.valuesObject.entries、字符串填充
  3. ES9 (2018) :异步迭代器、for-await-ofPromise.finally、正则命名捕获
  4. ES10 (2019)Array.prototype.flat / flatMapObject.fromEntries、字符串前后裁剪
  5. ES11 (2020) :可选链 ?.、空值合并 ??
  6. ES12 (2021) :逻辑赋值 ||= / &&= / ??=Promise.allSettled

年度更新(ES2025/ES2026)

  • Iterator Helpers(ES2025) :迭代器原生支持 .map().filter().take() 等惰性管道方法,适合数据流与大集合处理。[TC39/ES2025、Saeloun]
  • Set 扩展(ES2025) :集合数学运算 unionintersectiondifferencesymmetricDifference 以及关系判定 isSubsetOfisSupersetOfisDisjointFrom。[InfoWorld、Saeloun]
  • JSON 模块(ES2025) :允许直接 import data from './data.json',减少构建与手动解析。[InfoWorld]
  • Temporal(ES2026 预计) :现代日期时间 API,替代历史 Date 的精度与时区问题。[The New Stack / TC39 草案]
  • Intl 与模块加载优化(ES2026 预计) :如 Intl.Locale 变体与 import defer,改进国际化与加载性能。[The New Stack]

新的变量声明方式

let

  • 块级作用域,值可重新赋值
  • 同一作用域不可重复声明
  • 存在暂时性死区
javascript 复制代码
let age;
age = 30;
console.log(age); // 30

const

  • 块级作用域常量,声明时必须初始化,引用不可重新绑定
  • 基本类型值不可变;对象/数组引用不可变但内容可变
javascript 复制代码
const age = 30;
const obj = { key: 'value' };
obj.key = 'newValue';

var 的对比

  • var 具函数/全局作用域;可重复声明;存在变量提升与全局污染风险
    let/const 避免上述问题,推荐优先使用。

实战中,比较常见的一条经验是:

  • 默认使用 const,仅在确实需要重新赋值时改用 let
  • 避免在同一作用域混用多个声明同名变量,以减少"阴影变量"导致的逻辑混淆

箭头函数

  • 更简洁的函数定义;单表达式可隐式返回
  • this、无 arguments;不适作构造函数;call/apply/bind 无效
javascript 复制代码
const multiply = (x, y) => x * y;
const double = x => x * 2;

默认参数与模板字符串

默认参数允许在函数声明时为形参提供"兜底值",避免在函数体内部编写大量 name = name || 'xxx' 之类的兼容代码。 需要注意的是,默认值只在实参为 undefined 时才会生效,传入 null 会被视作"有效值"。

模板字符串则通过反引号与 ${} 插值语法,将字符串拼接与表达式求值结合起来, 非常适合用于构建多行文本、日志、SQL/GraphQL 片段等结构化内容。

javascript 复制代码
function greet(name = 'World') {
  console.log(`Hello, ${name}!`);
}
greet();

展开运算符与解构赋值

展开运算符用于在"期望一串元素或键值对"的地方铺开数组或对象, 是构建新数组/对象和实现浅拷贝的利器;解构赋值则提供了从复杂结构中按模式提取字段的语法糖, 大幅减少中间变量与重复访问。

需要牢记两点:

  • 展开/解构都是浅层操作,嵌套对象仍然是共享引用
  • 解构时可以配合默认值与重命名,提升代码的自文档性
javascript 复制代码
const arr = [1, 2, 3];
const [a, ...b] = arr;       // a=1, b=[2,3]

const obj = { x: 1, y: 2, z: 3 };
const { x, ...y } = obj;     // x=1, y={y:2, z:3}

类(class)

声明与静态方法

javascript 复制代码
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();

继承与 super

javascript 复制代码
class 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 与私有

javascript 复制代码
class PersonX {
  get age() { return this._age; }
  set age(v) { if (v > 0) this._age = v; }
}

ES6 本身无原生私有字段,可通过 Symbol 或闭包模拟;后续版本已标准化类私有字段(以 # 形式)与静态初始化等能力(属于 ESNext/后续快照)。

模块化

导出

javascript 复制代码
export function add(x, y) { return x + y; }
export const PI = 3.14;
const tools = {};
export default tools;

导入

javascript 复制代码
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 逐步产生值
javascript 复制代码
function* gen() {
  yield 1; yield 2; yield 3;
}
[...gen()]; // [1,2,3]

Proxy 与 Reflect

  • Proxy:为对象提供拦截器,定义访问/赋值/函数调用等行为
  • Reflect :与 Proxy 配套的基本操作集合,提升一致性与可预测性
javascript 复制代码
const target = { x: 1 };
const p = new Proxy(target, {
  get(obj, key) { return key in obj ? obj[key] : undefined; }
});
p.x; // 1

类私有字段与静态块(ESNext)

  • 私有字段 :使用 #name 声明,外部不可访问
  • 静态初始化块:在类加载时执行一次的初始化逻辑
javascript 复制代码
class Counter {
  #value = 0;
  inc() { this.#value++; }
  get value() { return this.#value; }
  static {
    // 静态初始化
  }
}

顶层 await 与 Import Assertions

  • 顶层 await :在模块顶层使用 await,简化初始化流程(仅模块可用)
  • Import Assertions :为导入资源提供类型断言(如 assert { type: 'json' }
javascript 复制代码
const data = await fetch('/config.json').then(r => r.json());
import config from './config.json' assert { type: 'json' };

异步与 Promise(简述)

javascript 复制代码
const myPromise = new Promise((resolve, reject) => {
  // ...
  resolve('value');
});
myPromise.then(v => { /* ... */ }).catch(err => { /* ... */ });

链式调用:

javascript 复制代码
fetch('https://api.example.com/data')
  .then(r => r.json())
  .then(data => { /* ... */ })
  .catch(err => { /* ... */ });

静态方法:

javascript 复制代码
Promise.all([p1, p2, p3]).then(results => { /* ... */ });
Promise.race([p1, p2]).then(result => { /* ... */ });

async/await

javascript 复制代码
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
  } catch (error) {
    // ...
  }
}

Map / WeakMap

javascript 复制代码
const map = new Map();
map.set(key, value);
map.get(key);
map.has(key);
map.delete(key);
map.forEach((value, key) => { /* ... */ });

WeakMap:键必须是对象;弱引用;不可遍历;无 size

Set / WeakSet

javascript 复制代码
const 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 共存。

javascript 复制代码
const mySymbol = Symbol('mySymbol');
const obj = { [mySymbol]: 'Only one key' };

ES2025/ES2026 详解与示例

Iterator Helpers(ES2025)

  • 在迭代器上直接使用 .map/.filter/.take/.drop/.flatMap 等方法
  • 惰性处理,适合大集合与流式数据
javascript 复制代码
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/isDisjointFrom
javascript 复制代码
const 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]));    // true

JSON 模块(ES2025)

  • 直接导入 JSON 作为模块,减少手动解析与构建步骤
  • 可配合 Import Assertions 明确类型
javascript 复制代码
import config from './config.json' assert { type: 'json' };
console.log(config.title);

Temporal(ES2026 预计)

  • 现代日期时间 API:精确、时区安全
  • 与模块初始化/调度结合,避免 Date 的历史坑
javascript 复制代码
const now = Temporal.Now.zonedDateTimeISO('Asia/Shanghai');
const deadline = now.add({ hours: 1 });

import defer(ES2026 预计)

  • 延迟部分导入的求值,用于非关键模块的初始化
javascript 复制代码
import defer analytics from './analytics.js';
// 完成主业务初始化后,再进行分析模块启动
queueMicrotask(() => analytics.start());

实践建议(ES6+ 到 ES2026)

  • 优先模块化与顶层 await,简化初始化流程
  • 在数据密集场景使用 Iterator Helpers,避免中间数组
  • 使用 Set 扩展实现集合运算,替换手写版本以提高可读性
  • 通过 JSON 模块直接消费配置与数据快照
  • 引入 Temporal 做时间计算与时区管理
  • 针对非关键路径采用 import defer 与空闲调度策略

生成器与异步迭代(进阶)

  • 将生成器与 for...of 结合打造数据管道
  • 异步迭代器支持 for await...of 逐块拉取数据
javascript 复制代码
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]
javascript 复制代码
async function* fetchPages(ids) {
  for (const id of ids) {
    const res = await fetch(`/api/${id}`);
    yield res.json();
  }
}

Proxy/Reflect 高级用法

  • 数据校验与只读视图
  • 变更跟踪与调试
javascript 复制代码
const model = { x: 1 };
const ro = new Proxy(model, {
  set() { throw new Error('read-only'); },
  get(obj, key) { return Reflect.get(obj, key); }
});

模块模式与实践

  • 入口(index)统一导出子模块
  • 动态导入配合路由与懒加载
javascript 复制代码
// src/index.js
export * from './math.js';
export * from './string.js';
javascript 复制代码
// 动态导入
const route = 'settings';
const page = await import(`./pages/${route}.js`);
page.render();

类装配与组合技巧

  • 通过静态工厂封装构造复杂对象
  • 组合优于继承,使用委托与小对象拼装
javascript 复制代码
class HttpClient {
  static fromToken(token) {
    return new HttpClient({ headers: { Authorization: `Bearer ${token}` } });
  }
  constructor(opts) { this.opts = opts; }
  get(url) { return fetch(url, this.opts); }
}
相关推荐
前端付豪1 小时前
Nest 项目小实践之注册登陆
前端·node.js·nestjs
用户9121917620611 小时前
日本股票K线图生成实战:基于API的完整对接方案
前端
牛奶2 小时前
JS随笔:基础语法与控制结构
前端·javascript
天蓝色的鱼鱼2 小时前
Node.js 中间层退潮:从“前端救星”到“成本噩梦”
前端·架构·node.js
货拉拉技术2 小时前
如何用 AI 做业务级 Code Review
前端·agent·前端工程化
李剑一2 小时前
前端圈子又出新东西了,大幅提升解析速度。尤雨溪推荐,但我不太推荐
前端
前端Hardy2 小时前
HTML&CSS&JS:基于定位的实时天气卡片
javascript·css·html
青屿ovo2 小时前
Vue前端页面版本检测解决方案
前端·vue.js
front_2 小时前
React Hook介绍
前端