构造函数中封装私有状态的核心是闭包实现数据隔离、原型实现行为复用:用 let/const 声明私有变量,通过特权方法访问;通用方法挂载 prototype 以节省内存;WeakMap 可支持继承下的私有状态管理。

在构造函数中封装私有状态,核心是让每个实例拥有独立的、不可被外部直接访问的数据,同时复用方法逻辑——闭包管“数据隔离”,原型管“行为复用”。这不是技巧堆砌,而是职责分明的设计选择。
用闭包捕获局部变量,避开 this 污染
构造函数内部声明的 let/const 变量天然形成闭包作用域,外部无法触达,只有构造函数内定义的方法能持续访问它。这比挂到 this 上更安全,也避免了属性名冲突或意外覆盖。
- 私有变量不写
this.xxx = xxx,而是直接let value = initial - 需要暴露的操作,通过返回的特权方法(如
this.getValue = () => value)间接读取 - 若需修改,提供
this.increment = () => value++这类闭包内方法,而非暴露原始变量
把通用方法挂到 prototype,别重复创建
如果只靠闭包返回对象(如 return { increment() { ... } }),每次 new 都新建一套函数,内存浪费明显。把逻辑一致的方法统一放在原型上,既节省空间,又支持后续动态更新。
- 例如
Counter.prototype.reset = function() { this.value = 0; }—— 但注意:这里不能直接改闭包变量,所以得配合设计 - 更稳妥的做法是:闭包里提供 setter/getter 方法(如
this.setValue = v => value = v),原型方法调用这些接口 - 所有实例共享同一个原型对象,方法只存一份,
this自动绑定到当前实例
避免原型上存可变引用,防止共享陷阱
如果构造函数里初始化了数组或对象(如 this.items = []),再用 Child.prototype = new Parent() 做继承,所有子实例会共用同一份引用——这是常见 bug 来源。
- 解决方案之一:在构造函数中用闭包重新生成独立副本,比如
let items = [],再通过闭包方法管理 - 另一方案:用
WeakMap映射实例与私有数据,原型方法通过privateData.get(this)获取对应状态,干净且可继承 - 不推荐把
items直接挂到原型上,也不建议在构造函数里反复 new 同一结构
结合 WeakMap 实现可继承的私有状态
当需要支持继承又想保持私有性时,WeakMap 是比纯闭包更灵活的选择:它允许原型方法访问实例专属数据,且不影响原型链完整性。
- 定义一个
const privateData = new WeakMap(),作用域外不可见 - 构造函数中
privateData.set(this, { count: 0, config: options }) - 原型上的方法如
increment()内部调用privateData.get(this).count++ - WeakMap 的键必须是对象,自动随实例回收,无内存泄漏风险

















