JavaScript实现私有属性的核心是函数作用域封闭变量,主要方式包括闭包封装、模块作用域、WeakMap和ES2022私有字段#field,各自适用于不同场景。

JavaScript 中没有原生的 private 关键字(ES2022 起虽有私有字段 #field,但作用域机制仍是底层基础),真正实现私有属性保护的核心手段是**利用函数作用域封闭变量,阻止外部直接访问**。
闭包封装:最经典、兼容性最好的方式
通过在构造函数或工厂函数内部声明变量,仅暴露依赖这些变量的特定方法,外部无法触达变量本身。
- 变量定义在函数体内,属于该函数的局部作用域;
- 返回的对象方法(如 getter/setter)形成闭包,能持续访问该作用域中的变量;
- 外部代码无法通过对象实例访问到这些变量,连
for...in或Object.keys()都遍历不到。
示例:
function Counter() {
let count = 0; // 私有变量,外部不可访问
return {
increment() { count++; },
decrement() { count--; },
getValue() { return count; }
};
}
const c = Counter();
c.increment();
console.log(c.getValue()); // 1
console.log(c.count); // undefined —— 完全不可见
模块作用域(ES Module):天然隔离的私有环境
将变量声明在模块顶层(非导出),只通过 export 暴露必要接口,其他变量自动成为模块级私有成员。
- 适合工具函数、配置管理、单例状态等场景;
- 所有导入该模块的代码共享同一份私有变量(类似静态私有);
- 无需闭包,语义清晰,现代项目首选之一。
示例(utils.js):
// utils.js
let _cache = new Map(); // 模块内私有,不被导出
export function getCached(key) {
return _cache.get(key);
}
export function setCached(key, value) {
_cache.set(key, value);
}
// _cache 不会被导出,外部无法访问
WeakMap 实现实例级私有数据(适合类结构)
当使用 class 语法时,可借助 WeakMap 将私有数据与实例绑定,避免污染实例自身属性,且支持垃圾回收。
- 每个实例对应一个键(实例本身),值为私有数据对象;
- 外部拿不到
WeakMap实例,也无法用实例反查私有数据(除非持有 map 引用); - 比闭包更易组织面向对象逻辑,兼容 class 语法。
示例:
const privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name }); // 存入私有数据
}
getName() {
return privateData.get(this).name;
}
setName(newName) {
privateData.get(this).name = newName;
}
}
(补充)ES2022 私有字段 #field:语法糖,底层仍靠作用域隔离
# 开头的字段是真正的语言级私有,只能在类内部访问,JS 引擎强制限制,连 Reflect.ownKeys() 都不可见。
- 写法简洁,意图明确;
- 本质仍是运行时作用域控制——引擎在类定义时创建了隐式私有作用域;
- 注意:私有字段不能动态计算名(
this[#key]不合法),也不支持私有方法被继承。
示例:
class BankAccount {
#balance = 0;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance; // ✅ 可访问
}
}
const acc = new BankAccount();
acc.deposit(100);
console.log(acc.#balance); // ❌ SyntaxError:非法访问私有字段
不复杂但容易忽略:私有不是靠命名约定(如 _name),而是靠作用域能力切断访问路径。选哪种方式,取决于你的环境(是否支持私有字段)、代码组织风格(函数式 or 面向对象)和具体需求(实例级 or 模块级私有)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











