如何在JavaScript中不使用闭包实现封装?附示例
JavaScript 封装相关问题解答
下划线命名的属性是真私有吗?
下划线开头的属性只是社区约定,完全做不到真正的私有。早期JavaScript没有原生私有语法时,开发者用这个命名方式提醒他人「这是内部状态,不要直接修改」,但实际上外部代码可以随意读写这些属性:
const user = { _password: '123456' }; console.log(user._password); // 直接输出 '123456' user._password = '654321'; // 直接修改
不用闭包实现封装的可行方案
1. ES6 原生私有字段(推荐)
这是当前引擎原生支持的真正私有方案,无需闭包,语法上用#前缀标记私有字段,外部完全无法访问:
class Wallet { // 私有字段,仅类内部可访问 #balance = 0; // 公共接口方法 deposit(amount) { if (amount > 0) { this.#balance += amount; } } getBalance() { return this.#balance; } } const myWallet = new Wallet(); myWallet.deposit(100); console.log(myWallet.getBalance()); // 100 console.log(myWallet.#balance); // 抛出语法错误:私有字段不可在类外部访问
2. 结合 Object.freeze 的半私有实现
Object.freeze本身不能实现私有,但可以冻结对象的公共接口,防止外部新增/修改方法。搭配不可枚举属性,能实现类似封装的效果:
用 Symbol 作为内部状态的键
Symbol 的唯一性可以让外部难以直接访问内部状态,结合Object.freeze冻结公共方法:
function createTodoList() { const todosSymbol = Symbol('todos'); const todoList = { addTodo(text) { this[todosSymbol] = this[todosSymbol] || []; this[todosSymbol].push(text); }, getTodos() { return [...(this[todosSymbol] || [])]; // 返回副本防止外部修改内部数组 } }; // 冻结公共接口,禁止新增/修改方法 return Object.freeze(todoList); } const list = createTodoList(); list.addTodo('买牛奶'); console.log(list.getTodos()); // ['买牛奶'] // 外部无法直接拿到内部的todos数组,除非获取对应的Symbol console.log(Object.getOwnPropertySymbols(list)); // 能拿到Symbol,但属于非常规操作
用 Object.defineProperty 定义不可枚举属性
把内部状态设为不可枚举,这样常规遍历(比如Object.keys)看不到它,再冻结公共接口:
function createCounter() { const counter = { increment() { this._count++; }, getCount() { return this._count; } }; // 定义内部属性为不可枚举、可写但不可配置 Object.defineProperty(counter, '_count', { value: 0, writable: true, enumerable: false, configurable: false }); // 冻结公共方法,防止篡改 return Object.freeze(counter); } const counter = createCounter(); counter.increment(); console.log(counter.getCount()); // 1 // 常规遍历拿不到内部属性 console.log(Object.keys(counter)); // ['increment', 'getCount'] // 注意:还是能直接访问_counter,这只是约定性的半私有
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

