JavaScript原生无private/protected,ES2022起用#实现真私有,Symbol模拟弱私有,下划线约定模拟protected,并需结合文档、工具链与团队约定保障访问控制。

JavaScript原生不支持private和protected访问修饰符(ES2022起虽有私有字段,但仅限#field语法,且无protected),因此在类中模拟这两种访问控制,需结合语言特性与约定来实现。
用#号实现真正的private(ES2022+)
现代JavaScript通过井号#前缀定义私有字段和方法,它们仅在类内部可访问,外部读写均抛出SyntaxError或TypeError:
- 私有字段必须在类体中声明(如
#count = 0),不可动态添加 - 私有方法也以
#开头,可在构造函数、实例方法或其它私有方法中调用 - 子类无法继承或访问父类的
#成员,真正隔离
示例:
class Counter {
#count = 0;
#increment() { this.#count++; }
getCount() { return this.#count; }
add() { this.#increment(); }
}尝试new Counter().#count会报错,Counter.prototype.#count也不存在——这是目前最接近“真私有”的方案。
立即学习“Java免费学习笔记(深入)”;
用Symbol模拟弱私有(兼容旧环境)
在不支持#的环境中,可用Symbol作为对象属性键,配合闭包或模块作用域隐藏标识符:
- 每个
Symbol()都是唯一值,外部无法猜测或枚举(除非反射获取Object.getOwnPropertySymbols()) - 通常将
Symbol声明在类外部但同一作用域,避免暴露给调用方 - 注意:这不是强制访问控制,只是“约定不碰”,调试时仍可见
示例:
const _value = Symbol('value');
class Box {
constructor(val) {
this[_value] = val;
}
getValue() { return this[_value]; }
}模拟protected:靠命名约定 + 继承设计
JavaScript没有protected关键字,通用做法是:用下划线前缀(如_internal)表明“仅供子类使用”,并依赖开发者自觉:
- 父类中把应被子类复用的属性/方法命名为
_xxx,文档注明“protected” - 子类可自由访问
this._xxx,但外部代码不应直接调用 - 可配合JSDoc标注
@protected,供IDE提示(如VS Code、WebStorm) - 若需运行时提醒,可在getter/setter中加
console.warn或throw(仅开发环境)
示例:
class Animal {
constructor(name) {
this._name = name; // protected
}
_makeSound() { console.log('...'); } // protected method
}
<p>class Dog extends Animal {
bark() {
console.log(<code>${this._name} barks</code>);
this._makeSound(); // 允许在子类中调用
}
}组合策略:# + _ + 文档 + 工具链
生产项目中推荐分层处理:
- 核心敏感状态(如token、缓存键)用
#字段,杜绝外部篡改 - 子类扩展点(如钩子方法、可覆盖的配置)用
_前缀 + JSDoc说明 - 借助TypeScript(
private/protected仅作编译时检查)提升开发体验 - 用ESLint规则(如
no-restricted-syntax禁用this._xxx在非子类中出现)加强约束
不复杂但容易忽略:私有性本质是“谁该负责维护这个数据”,而非“能否被技术绕过”。合理划分边界、写清文档、统一团队约定,比纯技术封锁更有效。








