ES6 Class 本身不提供原生私有机制(ES2022 前),但可通过#私有字段(ES2022+)、Symbol、闭包及命名约定等四维组合实现有效封装,核心是控制访问边界、隐藏细节、防止篡改。

ES6 Class 本身不提供原生私有机制(ES2022 前),但“高级封装”核心在于**控制访问边界、隐藏实现细节、防止意外篡改**。真正有效的封装不是靠语法强制,而是靠设计意图与技术组合达成。下面从四个实用维度说明怎么做。
用 # 私有字段实现真正隔离(ES2022+)
现代 JavaScript 支持以 # 开头的私有成员,这是目前最简洁、最语义明确的封装方式:
- # 字段和方法仅在类定义内部可访问,子类无法继承,外部完全不可读写
- 必须在类体顶层声明(如 #id = 0),不能动态添加或通过字符串访问
- 私有方法可被其他实例方法调用,适合封装校验逻辑、状态转换等内部流程
示例:
class BankAccount {#balance = 0;
#log(msg) { console.log(`[LOG] ${msg}`); }
constructor(initial) {
if (initial this.#balance = initial;
this.#log(`账户创建,初始余额:${initial}`);
}
deposit(amount) {
if (amount this.#balance += amount;
return true;
}
getBalance() {
return this.#balance; // ✅ 公开只读接口
}
}
用 WeakMap 模拟私有状态(兼容旧环境)
当需支持 ES2022 之前环境时,WeakMap 是最健壮的模拟方案——它利用对象作为键,天然隔离实例私有数据:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 每个实例对应一个独立的 WeakMap 条目,外部无法通过引用获取其私有值
- WeakMap 的键是弱引用,不影响垃圾回收,避免内存泄漏
- 需在类外声明 WeakMap 实例,再在 constructor 中初始化私有数据
示例:
const privateData = new WeakMap();class Timer {
constructor() {
privateData.set(this, { startTime: Date.now(), isActive: false });
}
start() {
privateData.get(this).isActive = true;
}
elapsed() {
const { startTime } = privateData.get(this);
return Date.now() - startTime;
}
}
用 getter/setter 控制属性访问逻辑
即使属性本身是公开的,也可通过访问器注入约束、转换或副作用,这是封装行为而非数据的关键手段:
- setter 可做输入校验、格式标准化(如转小写、截断字符串)
- getter 可返回计算值、触发懒加载、或屏蔽原始字段(如返回副本防止外部修改)
- 配合 Symbol 或私有字段,能构建更安全的读写契约
示例:
class User {#email;
constructor(email) {
this.email = email; // 触发 setter
}
set email(value) {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
throw new Error('邮箱格式不合法');
}
this.#email = value.trim().toLowerCase();
}
get email() {
return this.#email;
}
}
用静态私有字段区分类级与实例级封装
静态私有字段(static #config)适用于封装类级别的常量、缓存或全局策略,和实例无关:
- 避免把配置硬编码在方法里,提升可维护性
- 静态私有字段对所有实例共享,但对外部完全不可见
- 结合静态方法,可构建受控的工厂或单例入口
示例:
class ApiClient {static #baseUrl = 'https://api.example.com';
static #timeout = 5000;
static create(options = {}) {
return new ApiClient({
baseUrl: options.baseUrl ?? ApiClient.#baseUrl,
timeout: options.timeout ?? ApiClient.#timeout
});
}
constructor({ baseUrl, timeout }) {
this.baseUrl = baseUrl;
this.timeout = timeout;
}
}

















