当前位置: 首页 > news >正文

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()); // 0

6.3 私有 getter/setter

class 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: () => {} }) </script>

9. 浏览器兼容性

环境支持版本
Chrome74+ (2019年4月)
Firefox90+ (2021年7月)
Safari14.1+ (2021年4月)
Edge79+
Node.js12+ (需要 --harmony 标志)
14.6+ 原生支持
TypeScript3.8+

10. 总结

特性说明
语法#fieldName定义私有字段
访问只能在类内部通过this.#field访问
继承子类不能访问父类私有字段
动态性不能动态添加私有字段
序列化私有字段不会出现在Object.keys()JSON.stringify()
适用场景需要真正封装的类属性、内部状态保护

一句话总结:

#私有字段是 JavaScript 原生的私有属性语法,提供了真正的封装性,比传统的下划线约定更安全,比 WeakMap 方案更简洁直观。

http://www.jsqmd.com/news/539339/

相关文章:

  • Minio新手必看:如何正确配置S3 API端口避免403错误(含常见问题排查)
  • 避坑指南:Android应用开发中5种常见的黑屏场景及解决方案(含SurfaceControl实战)
  • CentOS7下快速部署LibreNMS监控系统:从零配置到中文界面设置
  • GetQzonehistory完整指南:三步实现QQ空间历史说说一键备份
  • 用Python和Jieba打造招聘关键词共现网络:从数据清洗到可视化全流程
  • 导师推荐!盘点2026年学生热捧的一键生成论文工具
  • 微算法科技(NASDAQ: MLGO)支持区块链的工业物联网隐私保护新方案:基于格的可链接环签名技术
  • 【自动驾驶】从贝叶斯到卡尔曼:线性滤波的数学之美与工程实践
  • SaToken vs Shiro vs Spring Security:轻量级权限框架选型指南
  • 保姆级教程:在ROS2 Humble上,用Livox MID-360和FAST_LIO为小车搭建自主探索环境
  • 智能家居控制组件:打造本地网络下的格力空调智能管理方案
  • 3步解锁浏览器自动化革命:n8n-nodes-puppeteer让网页操作告别手动时代
  • 2026年3月充电桩品牌十大品牌权威榜单:聚焦全场景解决方案与平台整合能力 - 十大品牌推荐
  • LeagueAkari:基于LCU API的英雄联盟客户端工具集架构实现
  • 实战指南:Cesium 矢量数据(点、线、面)的样式定制与交互实现
  • 离线环境解决方案:OpenClaw+GLM-4.7-Flash在内网科研机构的应用
  • 智能家居避坑指南:Arduino光敏电阻+继电器控制实战(LCD1602显示调试技巧)
  • 突破设备壁垒:VR内容全流程格式转换与跨设备兼容指南
  • 2026年救援拖车服务推荐:蔚县常森信息咨询部,道路救援/高速拖车/平板拖车一站式解决方案 - 品牌推荐官
  • MySQL官方版本与分支版本深度对比:如何选择最适合你的数据库方案
  • Windows 10/11下DM8达梦数据库安装全攻略(含常见错误解决)
  • 2026年AI编程助手对比分析(Gemini,DeepSeek,通义灵码,豆包Marscode,文心快码等)组合拳才是高阶玩法
  • Doris 平滑升级实战指南:从1.2.1到2.0的关键步骤与避坑技巧
  • RAG检索增强技术进阶教程(非常详细),从向量数据库到知识图谱精通,收藏这一篇就够了!
  • 2026年防腐钢管厂家推荐:沧州友通管道有限公司,2PE/3PE/化工/石油/市政工程防腐钢管全覆盖 - 品牌推荐官
  • 还在为看不清鼠标点击位置而烦恼吗?这款开源工具让屏幕操作一目了然
  • Unpaywall浏览器扩展:科研工作者的终极免费文献下载指南
  • Comsol 仿真磁屏蔽:铁氧体的神奇作用
  • 2026年钢闸门厂家推荐:新河县禹佳水工机械,钢制/不锈钢/弧形/机闸一体钢闸门全系列供应 - 品牌推荐官
  • 5个实用技巧:如何用Jira和Trello提升软件项目工时估算准确率