JavaScript派生类如何在不暴露前提下访问或重写基类私有属性
问题结论
首先明确:JavaScript 原生的#私有字段规范无法满足你的全部限制要求,这是语言本身的设计特性,不是实现技巧可以绕过的。
原生#私有字段的限制
JS中#开头的私有字段是词法绑定的,仅在定义该字段的类的代码块内部可访问,子类哪怕定义同名#字段,也是完全独立的两个私有字段,父类内部的方法只会访问父类自身定义的#私有字段,这就是你直接在子类定义#waterLimit不生效的原因。
你给出的构造函数传参初始化方案是原生语法下唯一符合限制的实现方式,但它的缺陷也很明显:子类只能在初始化阶段传参修改值,后续无法再访问或修改父类的该私有属性。
符合所有限制的替代实现:WeakMap方案
如果可以接受不用原生#私有字段,用WeakMap实现的私有属性可以完全满足你的所有限制:
- 私有属性对外完全不可访问,没有暴露setter/getter
- 不需要复制基类方法属性
- 不需要用下划线约定的受保护变量
- 子类可以在类内部任意位置访问、修改基类的私有属性
实现代码
// 模块级WeakMap,不对外导出,外部无法访问 const privateProps = new WeakMap(); class CoffeeMachine { constructor() { // 初始化基类私有属性 privateProps.set(this, { waterLimit: 600, waterAmount: 0 }); } set waterAmount(amount) { const props = privateProps.get(this); if (amount < 0) { props.waterAmount = 0; } else if (amount > props.waterLimit) { props.waterAmount = props.waterLimit; } else { props.waterAmount = amount; } } get waterAmount() { return privateProps.get(this).waterAmount; } } class TeaMachine extends CoffeeMachine { constructor() { super(); // 子类内部直接修改基类的私有属性 privateProps.get(this).waterLimit = 400; } // 子类后续也可以任意访问修改该属性 updateLimit(newLimit) { if(newLimit > 0) { privateProps.get(this).waterLimit = newLimit; } } } // 测试 const teaBox = new TeaMachine(); teaBox.waterAmount = 1000; console.log(teaBox.waterAmount); // 输出400 teaBox.updateLimit(300); teaBox.waterAmount = 1000; console.log(teaBox.waterAmount); // 输出300
方案说明
- WeakMap以实例为key存储私有属性,只要不对外导出这个WeakMap,外部完全无法访问到实例的私有属性,符合完全私有的要求
- 子类和基类在同一个模块中(或者可以访问该WeakMap的作用域内),就可以任意访问修改私有属性,不需要暴露任何对外的访问接口
内容的提问来源于stack exchange,提问作者Ruban Prakash
相关产品推荐
相关产品推荐

