ES2022引入的私有字段(#)语法为JavaScript类提供了真正的私有属性支持。私有字段通过#前缀定义只能在类内部访问外部无法通过任何方式访问或修改。相比传统的下划线约定、Symbol或WeakMap方案私有字段具有更好的封装性、更简洁的语法和更优的性能。私有字段支持静态字段、方法和getter/setter但不支持动态添加或子类继承访问。该特性已被主流浏览器和Node.js支持TypeScript 3.8也提供了兼容支持。私有字段是保护类内部状态的理想选择使JavaScript的面向对象编程更加完善。#是ES2022 (ES13)引入的私有字段Private Fields语法用于在类中定义真正的私有属性。1. 基本语法class MyClass { // 私有字段使用 # 前缀 #privateField 0; // 私有方法 #privateMethod() { return 私有方法; } // 公共方法可以访问私有字段 publicMethod() { console.log(this.#privateField); console.log(this.#privateMethod()); } } const instance new MyClass(); instance.publicMethod(); // ✅ 正常访问 console.log(instance.#privateField); // ❌ SyntaxError: 私有字段不能外部访问2. 为什么需要#私有字段在#出现之前JavaScript 没有真正的私有属性通常用以下方式模拟// ❌ 方式1: 下划线约定只是约定不是真正的私有 class User { constructor(name) { this._name name; // 约定为私有但仍可访问 } } const user new User(Alice); console.log(user._name); // Alice - 仍然可以访问 // ❌ 方式2: 闭包复杂且性能差 function createUser(name) { let _name name; // 真正的私有 return { getName() { return _name; }, setName(value) { _name value; } }; } // ✅ 方式3: 私有字段简洁且真正私有 class User { #name; constructor(name) { this.#name name; } getName() { return this.#name; } }3. 私有字段的特性3.1 真正的外部不可访问class BankAccount { #balance 0; deposit(amount) { this.#balance amount; } getBalance() { return this.#balance; } } const account new BankAccount(); account.deposit(100); console.log(account.getBalance()); // 100 // ❌ 以下都会报错 console.log(account.#balance); // SyntaxError console.log(account[#balance]); // undefined不是真正的属性名 console.log(Object.keys(account)); // [] - 私有字段不会出现在对象属性中3.2 硬性私有无法绕过class Secret { #password 123456; getPassword() { return this.#password; } } const secret new Secret(); // 所有尝试都无法访问私有字段 console.log(secret.#password); // SyntaxError console.log(secret[#password]); // undefined console.log(Reflect.get(secret, #password)); // undefined console.log(Object.getOwnPropertyNames(secret)); // [] console.log(JSON.stringify(secret)); // {}3.3 只能在类内部访问class Parent { #private parent private; parentMethod() { console.log(this.#private); // ✅ 父类内部可访问 } } class Child extends Parent { childMethod() { // ❌ 子类不能直接访问父类的私有字段 console.log(this.#private); // SyntaxError } }4. 私有字段 vs 其他方式对比特性#私有字段_约定SymbolWeakMap真正私有✅ 完全私有❌ 仅约定⚠️ 可绕过✅ 真正私有语法简洁✅ 简洁✅ 简洁⚠️ 稍复杂❌ 复杂性能✅ 优秀✅ 优秀✅ 优秀⚠️ 稍差调试友好✅ DevTools 支持✅ 可见⚠️ 难调试❌ 难调试继承支持⚠️ 子类不能访问✅ 可访问✅ 可访问⚠️ 需手动处理TypeScript✅ 支持3.8✅ 支持✅ 支持✅ 支持5. 完整示例对比// 方式1: 下划线约定 class UserV1 { constructor(name, age) { this._name name; // 约定私有 this._age age; } getInfo() { return ${this._name}, ${this._age}; } } // ❌ 外部仍可访问 const user1 new UserV1(Alice, 25); console.log(user1._name); // Alice - 没真正隐藏 // 方式2: Symbol const _name Symbol(name); const _age Symbol(age); class UserV2 { constructor(name, age) { this[_name] name; this[_age] age; } getInfo() { return ${this[_name]}, ${this[_age]}; } } // ⚠️ 仍可绕过 const user2 new UserV2(Alice, 25); const symbols Object.getOwnPropertySymbols(user2); console.log(user2[symbols[0]]); // Alice - 可以访问 // 方式3: WeakMap真正的私有 const privateData new WeakMap(); class UserV3 { constructor(name, age) { privateData.set(this, { name, age }); } getInfo() { const data privateData.get(this); return ${data.name}, ${data.age}; } } // ✅ 外部无法访问 const user3 new UserV3(Alice, 25); console.log(user3.name); // undefined console.log(privateData.get(user3)); // 需要 WeakMap 实例无法访问 // 方式4: 私有字段最简洁 class UserV4 { #name; #age; constructor(name, age) { this.#name name; this.#age age; } getInfo() { return ${this.#name}, ${this.#age}; } } // ✅ 完全私有语法最简洁 const user4 new UserV4(Alice, 25); console.log(user4.#name); // SyntaxError - 无法访问6. 私有字段的高级用法6.1 私有方法class Calculator { #result 0; // 私有方法 #validateNumber(num) { if (typeof num ! number) { throw new Error(参数必须是数字); } return num; } // 私有方法 #updateResult(value) { this.#result value; } // 公共方法 add(num) { const validNum this.#validateNumber(num); this.#updateResult(this.#result validNum); return this; } getResult() { return this.#result; } } const calc new Calculator(); calc.add(5).add(3); console.log(calc.getResult()); // 8 // calc.#validateNumber(10); // ❌ 无法调用私有方法6.2 私有静态字段class Counter { // 私有静态字段 static #count 0; constructor() { Counter.#count; } static getCount() { return Counter.#count; } // 私有静态方法 static #reset() { Counter.#count 0; } static resetCount() { Counter.#reset(); } } console.log(Counter.getCount()); // 0 new Counter(); new Counter(); console.log(Counter.getCount()); // 2 Counter.resetCount(); console.log(Counter.getCount()); // 06.3 私有 getter/setterclass User { #firstName; #lastName; constructor(firstName, lastName) { this.#firstName firstName; this.#lastName lastName; } // 私有 getter get #fullName() { return ${this.#firstName} ${this.#lastName}; } // 公共方法可以使用私有 getter getProfile() { return { name: this.#fullName, initials: ${this.#firstName[0]}.${this.#lastName[0]}. }; } } const user new User(Alice, Lee); console.log(user.getProfile()); // { name: Alice Lee, initials: A.L. } // user.#fullName ❌ 无法访问7. 私有字段的注意事项7.1 不能动态创建class MyClass { #field 1; addField() { // ❌ 不能动态创建私有字段 this.#dynamic 2; // SyntaxError } }7.2 命名唯一性class MyClass { #value 1; method() { // ✅ 同一个类中可以使用多次 console.log(this.#value); this.#value 2; } } // ❌ 不同类之间的同名私有字段不冲突 class OtherClass { #value 100; // 这是另一个私有字段 }7.3 序列化行为class User { #password secret; name Alice; toJSON() { return { name: this.name, // 需要手动暴露私有字段 // #password 不会自动序列化 }; } } const user new User(); console.log(JSON.stringify(user)); // {name:Alice} console.log(Object.keys(user)); // [name]8. 在 Vue 3 中的应用Composition API 通过模块作用域、闭包或readonly实现状态封装script setup import { ref, readonly, computed } from vue // 1. 模块级私有最彻底 const API_KEY secret function privateHelper() {} // 2. 组件内私有仅当前组件可访问 const internalState ref(0) const internalMethod () {} // 3. 公开状态使用 readonly 保护 const publicState ref(visible) const protectedState readonly(publicState) // 4. 通过 defineExpose 控制暴露 defineExpose({ protectedState, publicMethod: () {} }) /script9. 浏览器兼容性环境支持版本Chrome74 (2019年4月)Firefox90 (2021年7月)Safari14.1 (2021年4月)Edge79Node.js12 (需要 --harmony 标志)14.6 原生支持TypeScript3.810. 总结特性说明语法#fieldName定义私有字段访问只能在类内部通过this.#field访问继承子类不能访问父类私有字段动态性不能动态添加私有字段序列化私有字段不会出现在Object.keys()和JSON.stringify()中适用场景需要真正封装的类属性、内部状态保护一句话总结#私有字段是 JavaScript 原生的私有属性语法提供了真正的封装性比传统的下划线约定更安全比 WeakMap 方案更简洁直观。
ES13 # 私有字段( Private Fields) 语法:在类中定义真正的私有属性
ES2022引入的私有字段(#)语法为JavaScript类提供了真正的私有属性支持。私有字段通过#前缀定义只能在类内部访问外部无法通过任何方式访问或修改。相比传统的下划线约定、Symbol或WeakMap方案私有字段具有更好的封装性、更简洁的语法和更优的性能。私有字段支持静态字段、方法和getter/setter但不支持动态添加或子类继承访问。该特性已被主流浏览器和Node.js支持TypeScript 3.8也提供了兼容支持。私有字段是保护类内部状态的理想选择使JavaScript的面向对象编程更加完善。#是ES2022 (ES13)引入的私有字段Private Fields语法用于在类中定义真正的私有属性。1. 基本语法class MyClass { // 私有字段使用 # 前缀 #privateField 0; // 私有方法 #privateMethod() { return 私有方法; } // 公共方法可以访问私有字段 publicMethod() { console.log(this.#privateField); console.log(this.#privateMethod()); } } const instance new MyClass(); instance.publicMethod(); // ✅ 正常访问 console.log(instance.#privateField); // ❌ SyntaxError: 私有字段不能外部访问2. 为什么需要#私有字段在#出现之前JavaScript 没有真正的私有属性通常用以下方式模拟// ❌ 方式1: 下划线约定只是约定不是真正的私有 class User { constructor(name) { this._name name; // 约定为私有但仍可访问 } } const user new User(Alice); console.log(user._name); // Alice - 仍然可以访问 // ❌ 方式2: 闭包复杂且性能差 function createUser(name) { let _name name; // 真正的私有 return { getName() { return _name; }, setName(value) { _name value; } }; } // ✅ 方式3: 私有字段简洁且真正私有 class User { #name; constructor(name) { this.#name name; } getName() { return this.#name; } }3. 私有字段的特性3.1 真正的外部不可访问class BankAccount { #balance 0; deposit(amount) { this.#balance amount; } getBalance() { return this.#balance; } } const account new BankAccount(); account.deposit(100); console.log(account.getBalance()); // 100 // ❌ 以下都会报错 console.log(account.#balance); // SyntaxError console.log(account[#balance]); // undefined不是真正的属性名 console.log(Object.keys(account)); // [] - 私有字段不会出现在对象属性中3.2 硬性私有无法绕过class Secret { #password 123456; getPassword() { return this.#password; } } const secret new Secret(); // 所有尝试都无法访问私有字段 console.log(secret.#password); // SyntaxError console.log(secret[#password]); // undefined console.log(Reflect.get(secret, #password)); // undefined console.log(Object.getOwnPropertyNames(secret)); // [] console.log(JSON.stringify(secret)); // {}3.3 只能在类内部访问class Parent { #private parent private; parentMethod() { console.log(this.#private); // ✅ 父类内部可访问 } } class Child extends Parent { childMethod() { // ❌ 子类不能直接访问父类的私有字段 console.log(this.#private); // SyntaxError } }4. 私有字段 vs 其他方式对比特性#私有字段_约定SymbolWeakMap真正私有✅ 完全私有❌ 仅约定⚠️ 可绕过✅ 真正私有语法简洁✅ 简洁✅ 简洁⚠️ 稍复杂❌ 复杂性能✅ 优秀✅ 优秀✅ 优秀⚠️ 稍差调试友好✅ DevTools 支持✅ 可见⚠️ 难调试❌ 难调试继承支持⚠️ 子类不能访问✅ 可访问✅ 可访问⚠️ 需手动处理TypeScript✅ 支持3.8✅ 支持✅ 支持✅ 支持5. 完整示例对比// 方式1: 下划线约定 class UserV1 { constructor(name, age) { this._name name; // 约定私有 this._age age; } getInfo() { return ${this._name}, ${this._age}; } } // ❌ 外部仍可访问 const user1 new UserV1(Alice, 25); console.log(user1._name); // Alice - 没真正隐藏 // 方式2: Symbol const _name Symbol(name); const _age Symbol(age); class UserV2 { constructor(name, age) { this[_name] name; this[_age] age; } getInfo() { return ${this[_name]}, ${this[_age]}; } } // ⚠️ 仍可绕过 const user2 new UserV2(Alice, 25); const symbols Object.getOwnPropertySymbols(user2); console.log(user2[symbols[0]]); // Alice - 可以访问 // 方式3: WeakMap真正的私有 const privateData new WeakMap(); class UserV3 { constructor(name, age) { privateData.set(this, { name, age }); } getInfo() { const data privateData.get(this); return ${data.name}, ${data.age}; } } // ✅ 外部无法访问 const user3 new UserV3(Alice, 25); console.log(user3.name); // undefined console.log(privateData.get(user3)); // 需要 WeakMap 实例无法访问 // 方式4: 私有字段最简洁 class UserV4 { #name; #age; constructor(name, age) { this.#name name; this.#age age; } getInfo() { return ${this.#name}, ${this.#age}; } } // ✅ 完全私有语法最简洁 const user4 new UserV4(Alice, 25); console.log(user4.#name); // SyntaxError - 无法访问6. 私有字段的高级用法6.1 私有方法class Calculator { #result 0; // 私有方法 #validateNumber(num) { if (typeof num ! number) { throw new Error(参数必须是数字); } return num; } // 私有方法 #updateResult(value) { this.#result value; } // 公共方法 add(num) { const validNum this.#validateNumber(num); this.#updateResult(this.#result validNum); return this; } getResult() { return this.#result; } } const calc new Calculator(); calc.add(5).add(3); console.log(calc.getResult()); // 8 // calc.#validateNumber(10); // ❌ 无法调用私有方法6.2 私有静态字段class Counter { // 私有静态字段 static #count 0; constructor() { Counter.#count; } static getCount() { return Counter.#count; } // 私有静态方法 static #reset() { Counter.#count 0; } static resetCount() { Counter.#reset(); } } console.log(Counter.getCount()); // 0 new Counter(); new Counter(); console.log(Counter.getCount()); // 2 Counter.resetCount(); console.log(Counter.getCount()); // 06.3 私有 getter/setterclass User { #firstName; #lastName; constructor(firstName, lastName) { this.#firstName firstName; this.#lastName lastName; } // 私有 getter get #fullName() { return ${this.#firstName} ${this.#lastName}; } // 公共方法可以使用私有 getter getProfile() { return { name: this.#fullName, initials: ${this.#firstName[0]}.${this.#lastName[0]}. }; } } const user new User(Alice, Lee); console.log(user.getProfile()); // { name: Alice Lee, initials: A.L. } // user.#fullName ❌ 无法访问7. 私有字段的注意事项7.1 不能动态创建class MyClass { #field 1; addField() { // ❌ 不能动态创建私有字段 this.#dynamic 2; // SyntaxError } }7.2 命名唯一性class MyClass { #value 1; method() { // ✅ 同一个类中可以使用多次 console.log(this.#value); this.#value 2; } } // ❌ 不同类之间的同名私有字段不冲突 class OtherClass { #value 100; // 这是另一个私有字段 }7.3 序列化行为class User { #password secret; name Alice; toJSON() { return { name: this.name, // 需要手动暴露私有字段 // #password 不会自动序列化 }; } } const user new User(); console.log(JSON.stringify(user)); // {name:Alice} console.log(Object.keys(user)); // [name]8. 在 Vue 3 中的应用Composition API 通过模块作用域、闭包或readonly实现状态封装script setup import { ref, readonly, computed } from vue // 1. 模块级私有最彻底 const API_KEY secret function privateHelper() {} // 2. 组件内私有仅当前组件可访问 const internalState ref(0) const internalMethod () {} // 3. 公开状态使用 readonly 保护 const publicState ref(visible) const protectedState readonly(publicState) // 4. 通过 defineExpose 控制暴露 defineExpose({ protectedState, publicMethod: () {} }) /script9. 浏览器兼容性环境支持版本Chrome74 (2019年4月)Firefox90 (2021年7月)Safari14.1 (2021年4月)Edge79Node.js12 (需要 --harmony 标志)14.6 原生支持TypeScript3.810. 总结特性说明语法#fieldName定义私有字段访问只能在类内部通过this.#field访问继承子类不能访问父类私有字段动态性不能动态添加私有字段序列化私有字段不会出现在Object.keys()和JSON.stringify()中适用场景需要真正封装的类属性、内部状态保护一句话总结#私有字段是 JavaScript 原生的私有属性语法提供了真正的封装性比传统的下划线约定更安全比 WeakMap 方案更简洁直观。