0

0

ES6的私有类字段如何实现封装

星降

星降

发布时间:2025-07-14 11:57:02

|

855人浏览过

|

来源于php中文网

原创

es6私有类字段通过#符号实现真正的封装,与传统下划线约定的本质区别在于强制访问限制。1. 下划线前缀(如\_name)仅是命名约定,外部仍可随意访问或修改;2. #符号声明的私有字段只能在类内部访问,外部尝试访问会抛出语法错误。这种语言层面的强制封装提升了代码的健壮性和可维护性,尤其适用于构建公共api、保护敏感数据、执行业务逻辑、避免命名冲突及清晰职责分离等场景。此外,私有字段不会被子类继承,确保父类内部状态的安全性,强化了面向对象中封装原则的实现。

ES6的私有类字段如何实现封装

ES6的私有类字段通过在字段名前加上#符号来实现真正的封装,这意味着这些字段只能在类的内部被访问和修改,外部代码无法直接触及,从而有效保护了数据的内部状态。

ES6的私有类字段如何实现封装

解决方案

在JavaScript的世界里,长期以来我们都依赖约定俗成的下划线前缀(如_privateField)来表示一个属性是“私有的”,但这终究只是君子协定,外部依然可以随意访问甚至修改。ES6(或者更准确地说是ES2022,因为这是类私有字段正式成为标准的一部分)引入的私有类字段彻底改变了这一点。

使用起来非常直观,你只需要在类的属性或方法名前面加上一个#号。一旦加上,这个属性或方法就成了私有成员,只能在声明它的类内部被访问。

ES6的私有类字段如何实现封装
class BankAccount {
  #balance; // 私有字段
  #transactions = []; // 另一个私有字段

  constructor(initialBalance) {
    if (initialBalance < 0) {
      throw new Error("Initial balance cannot be negative.");
    }
    this.#balance = initialBalance;
  }

  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      this.#transactions.push({ type: 'deposit', amount, date: new Date() });
      console.log(`Deposited ${amount}. New balance: ${this.#balance}`);
    }
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
      this.#transactions.push({ type: 'withdraw', amount, date: new Date() });
      console.log(`Withdrew ${amount}. New balance: ${this.#balance}`);
      return true;
    }
    console.log("Insufficient funds or invalid amount.");
    return false;
  }

  // 内部使用的私有方法
  #logTransaction(type, amount) {
    console.log(`[Internal Log] ${type}: ${amount}`);
  }

  getAccountInfo() {
    // 可以在内部访问私有字段
    this.#logTransaction('info_request', null);
    return `Current balance: ${this.#balance}. Total transactions: ${this.#transactions.length}`;
  }
}

const myAccount = new BankAccount(100);
myAccount.deposit(50);
// console.log(myAccount.#balance); // 尝试访问会报错:SyntaxError: Private field '#balance' must be declared in an enclosing class
// myAccount.#logTransaction('test', 10); // 同样报错

console.log(myAccount.getAccountInfo());

这段代码里,#balance#transactions是真正意义上的私有。你无法从myAccount实例的外部直接访问它们。任何试图这么做的行为,都会在运行时抛出SyntaxError。这和那些仅仅是“建议私有”的下划线属性有着本质的区别。对我个人来说,这种强制性封装,让代码的健壮性和可维护性得到了极大的提升,尤其是在构建大型应用或公共库的时候,真的能省去不少麻烦。

ES6私有字段与传统下划线命名约定有何本质区别?

说实话,在私有字段出来之前,我一直觉得JavaScript在封装性上有点“软”。我们习惯用下划线前缀(比如_name)来暗示一个属性是内部使用的,不建议外部直接访问。但这充其量只是一种约定,一种开发者之间的“君子协定”。任何时候,你都可以直接通过object._name来访问甚至修改它。这在团队协作中,或者当项目规模变大时,很容易导致意外的副作用或者难以追踪的bug。

ES6的私有类字段如何实现封装

比如:

class OldSchoolPerson {
  constructor(name) {
    this._name = name; // 约定俗成的私有属性
  }

  getName() {
    return this._name;
  }
}

const person = new OldSchoolPerson("Alice");
console.log(person.getName()); // Alice
console.log(person._name);     // Alice (可以访问)
person._name = "Bob";          // 甚至可以修改!
console.log(person.getName()); // Bob

你看,_name虽然带着下划线,但它和普通属性没什么两样。任何人都能轻易地绕过你设计的API去直接操作它。

而ES6的私有字段(#name)则提供了一种语言层面的强制封装。当你声明一个字段为#name时,它就真正地被“锁”在了类的内部。外部代码,包括子类,都无法直接访问它。尝试访问会直接抛出语法错误。这就像给你的私密数据加了一道坚固的门,而不是仅仅贴了个“非请勿入”的标签。这种强制性,我认为是ES6私有字段最核心的价值所在,它让开发者能够真正地构建出具有清晰边界和高内聚性的模块。这不仅仅是语法上的变化,更是对面向对象编程中“封装”原则的强力支持。

在实际开发中,何时应该优先使用ES6私有类字段?

对我而言,ES6私有类字段的最佳应用场景,就是当你需要确保某个类的内部状态或实现细节,绝对不被外部代码随意窥探或修改时。这听起来有点像废话,但实际开发中,这种需求比你想象的要普遍得多。

Faceswap
Faceswap

免费开源的AI换脸工具

下载

我列举几个我认为非常适合使用私有字段的场景:

  1. 构建健壮的公共API或库: 如果你在开发一个供他人使用的库或框架,那么封装性至关重要。你希望用户只能通过你提供的公共方法来与你的类交互,而不是直接操作内部数据。私有字段能有效防止用户“误用”或“滥用”你的内部实现,从而减少未来版本迭代时可能出现的兼容性问题。这有点像我设计一个工具,我希望你只用把手,而不是去拆开看里面的齿轮怎么转。
  2. 保护敏感数据: 比如在上面的BankAccount例子中,账户余额#balance就是非常敏感的数据。你肯定不希望外部能直接修改它,而只能通过depositwithdraw这样的受控方法来改变。私有字段在这里就提供了一层坚实的安全保障。
  3. 强制执行业务逻辑和数据完整性: 有时候,一个属性的改变需要伴随着一系列复杂的逻辑(比如验证、日志记录、触发其他事件等)。如果这个属性是私有的,那么所有对它的修改都必须通过公共方法,这些方法内部可以确保所有的业务规则都被遵守,从而维护数据的一致性和完整性。
  4. 避免命名冲突: 当你继承一个类或者在大型项目中引入第三方代码时,私有字段的命名空间是完全隔离的。这意味着你不用担心你的私有字段名会和父类或者其他模块的公共/私有属性名发生冲突。因为#前缀是独一无二的,它保证了字段名的唯一性,这在复杂的类结构中特别有用。
  5. 清晰的职责分离: 当一个字段被声明为私有时,它明确地告诉其他开发者:“这是这个类自己的事情,你不用关心,也不应该直接操作。”这有助于团队成员更好地理解代码的意图和边界,提升代码的可读性和可维护性。

总的来说,每当我觉得“这个东西就是这个类自己的秘密,谁都不能碰”的时候,我就会毫不犹豫地使用私有字段。它让我的代码意图表达得更清晰,也让我的类变得更可靠。

ES6私有字段在继承和多态中表现如何?

这其实是一个很有意思的话题,也是我刚开始接触私有字段时,有点困惑的地方。简单来说,ES6的私有字段在继承中表现得非常“私有”——它们是不被子类继承的

这意味着什么呢?当你有一个父类,里面定义了私有字段,然后你创建了一个子类继承它,子类的实例是无法直接访问父类的私有字段的。这和公共属性或受保护属性(如果JavaScript有的话)的行为是完全不同的。

看个例子可能更清楚:

class Animal {
  #name; // 父类的私有字段

  constructor(name) {
    this.#name = name;
  }

  // 父类内部可以访问自己的私有字段
  getName() {
    return this.#name;
  }
}

class Dog extends Animal {
  #breed; // 子类自己的私有字段

  constructor(name, breed) {
    super(name); // 调用父类构造函数,父类会初始化自己的 #name
    this.#breed = breed;
  }

  getDogInfo() {
    // return `Name: ${this.#name}, Breed: ${this.#breed}`; // 错误:无法访问父类的 #name
    // 但可以通过父类的公共方法访问父类的私有数据
    return `Name: ${super.getName()}, Breed: ${this.#breed}`;
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.getDogInfo()); // Name: Buddy, Breed: Golden Retriever
// console.log(myDog.#name); // 报错:SyntaxError: Private field '#name' must be declared in an enclosing class

从上面的例子可以看出,Dog类无法直接访问Animal类的#name字段。如果它想获取这个名字,必须通过Animal类提供的公共方法(比如getName())。这完全符合“封装”的原则:私有成员是类自己的内部实现细节,即使是子类,也应该通过父类定义的公共接口来与其交互。

这种设计选择其实很合理。如果子类能直接访问父类的私有字段,那么父类内部的封装性就会被打破,父类作者就无法保证其内部状态的完整性。这会让类的设计变得混乱,也难以维护。私有字段的这种行为,强制你思考清楚哪些数据是真正的内部实现,哪些是应该通过公共接口暴露出去的。对我来说,这是一种设计上的约束,它促使我写出更清晰、更可维护的类结构。

在多态性方面,私有字段本身并不直接参与多态。多态主要体现在方法上,即不同类的对象可以对同一消息(方法调用)做出不同的响应。私有字段是数据,它们是内部状态的一部分,不负责行为的外部表现。不过,私有字段的存在,使得你能够更安全地在父类中定义和使用内部数据,而不必担心这些数据会意外地影响到子类的行为,从而间接支持了更健壮的多态实现。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

103

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

195

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2025.12.24

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

142

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.2万人学习

Pandas 教程
Pandas 教程

共15课时 | 1.0万人学习

ASP 教程
ASP 教程

共34课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号