私有字段提案(#前缀)
ECMAScript 6 私有字段提案的背景
ECMAScript 6 引入了类的概念,但类的成员默认是公开的。为了解决封装性问题,TC39 委员会提出了私有字段提案,使用 #
前缀标识私有成员。该提案最终被纳入 ECMAScript 2022 标准,成为 JavaScript 类定义中实现真正私有性的官方方案。
私有字段的基本语法
私有字段通过在字段名前添加 #
前缀来声明,且必须在类顶层显式声明。这种设计确保了私有性在语法层面的强制约束:
class Counter {
#count = 0; // 私有字段声明
increment() {
this.#count++;
}
get value() {
return this.#count;
}
}
const counter = new Counter();
counter.increment();
console.log(counter.value); // 1
console.log(counter.#count); // SyntaxError: Private field '#count' must be declared in an enclosing class
私有字段的特性
- 编译时检查:私有字段的访问限制在语法解析阶段就会生效
- 硬私有:无法通过任何运行时机制(如
Object.getOwnPropertySymbols
)访问 - 唯一性:每个类的私有字段都是独立的,子类无法访问父类的私有字段
- 必须先声明:所有私有字段必须在类顶层显式声明后才能使用
与 TypeScript private 的区别
TypeScript 的 private
修饰符只是编译时的约束,编译后的代码中这些字段仍然是公开的:
class TSClass {
private secret = 123;
}
const instance = new TSClass();
console.log(instance['secret']); // 123 (可以绕过类型系统访问)
而 ECMAScript 的私有字段是真正的运行时私有:
class JSClass {
#secret = 123;
}
const instance = new JSClass();
console.log(instance['#secret']); // undefined
私有字段的继承规则
私有字段遵循严格的封装规则,子类不能访问父类的私有字段:
class Parent {
#familySecret = 'parent secret';
reveal() {
return this.#familySecret;
}
}
class Child extends Parent {
#familySecret = 'child secret'; // 这是完全不同的字段
getParentSecret() {
// return super.#familySecret; // 语法错误
return super.reveal();
}
}
const child = new Child();
console.log(child.getParentSecret()); // "parent secret"
console.log(child.reveal()); // "child secret"
私有字段与方法
私有字段可以与私有方法结合使用,实现完全私有的实现细节:
class Auth {
#password = 'default';
#validate(password) {
return password.length >= 8;
}
setPassword(newPassword) {
if (this.#validate(newPassword)) {
this.#password = newPassword;
return true;
}
return false;
}
}
const auth = new Auth();
auth.setPassword('short'); // false
auth.setPassword('longenough'); // true
// auth.#validate('test'); // SyntaxError
私有字段与静态成员
静态私有字段通过在 static
关键字后添加 #
前缀实现:
class Logger {
static #logLevel = 'INFO';
static #log(message, level) {
if (level === this.#logLevel) {
console.log(message);
}
}
static info(message) {
this.#log(message, 'INFO');
}
}
Logger.info('This will be logged');
// Logger.#logLevel = 'DEBUG'; // SyntaxError
私有字段的限制
-
必须在类中预先声明:
class Example { constructor() { this.#value = 42; // SyntaxError: Private field '#value' must be declared in an enclosing class } }
-
不能动态创建:
class Dynamic { createPrivate() { this.#newField = 1; // SyntaxError } }
-
不能使用计算属性名:
const fieldName = 'private'; class Computed { #fieldName = 1; // 合法,但字段名就是 "#fieldName" #[fieldName] = 2; // SyntaxError }
私有字段的实际应用场景
-
实现内部状态封装:
class Stack { #items = []; #maxSize = 10; push(item) { if (this.#items.length < this.#maxSize) { this.#items.push(item); } } pop() { return this.#items.pop(); } }
-
隐藏实现细节:
class Temperature { #celsius = 0; set celsius(value) { this.#celsius = value; } get fahrenheit() { return this.#celsius * 1.8 + 32; } }
-
防止命名冲突:
class SafeMap { #data = new Map(); #size = 0; set(key, value) { this.#data.set(key, value); this.#size = this.#data.size; } }
私有字段与 WeakMap 方案的对比
在私有字段出现前,常用 WeakMap 模拟私有性:
const _data = new WeakMap();
class OldSchool {
constructor() {
_data.set(this, { count: 0 });
}
increment() {
const data = _data.get(this);
data.count++;
}
}
私有字段方案的优势:
- 语法更简洁
- 性能更好(引擎可以优化存储)
- 调试更友好(浏览器开发者工具支持私有字段检查)
私有字段的浏览器兼容性
截至 2023 年,所有现代浏览器都已支持私有字段:
- Chrome 84+
- Firefox 90+
- Safari 14.1+
- Edge 84+
- Node.js 12+
对于旧环境,需要通过 Babel 等工具转译。Babel 会将私有字段转换为 WeakMap 形式的实现。
私有字段的设计哲学
TC39 选择 #
前缀而非其他方案(如 private
关键字)的原因:
- 显式性:私有成员在语法上明显区别于公共成员
- 一致性:与现有语法(如
#
在 CSS 选择器中的使用)保持一定相似性 - 可扩展性:为未来可能的装饰器等特性留出设计空间
私有字段与 TypeScript 的配合使用
TypeScript 4.3+ 支持 ECMAScript 私有字段语法,并与类型系统集成:
class TypeSafe {
#value: number;
constructor(value: number) {
this.#value = value;
}
square(): number {
return this.#value ** 2;
}
}
const instance = new TypeSafe(5);
// instance.#value = "string"; // 编译时和运行时都会报错
私有字段的调试注意事项
现代浏览器开发者工具支持私有字段检查,但有以下特点:
- 私有字段显示为
#field
形式 - 不能通过控制台直接访问(保持私有性)
- 断点调试时可以查看私有字段值
class DebugExample {
#debugValue = 42;
method() {
debugger;
console.log(this.#debugValue);
}
}
new DebugExample().method();
// 在调试器中可以查看 #debugValue
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn