JS继承中安全封装的关键是区分实例属性(构造函数内this赋值确保隔离)与原型方法(复用不共享状态),ES6 class仅提供语法结构,底层仍依赖原型机制和构造逻辑控制。

JS 继承中实现安全的数据封装与隔离,关键不在“继承语法本身”,而在于明确区分实例属性(需隔离)和原型方法(可复用),并避免共享引用类型数据。ES6 class 提供了清晰结构,但真正起作用的仍是底层原型机制和构造逻辑的控制。
用构造函数初始化实例属性,确保数据隔离
父类中所有需要每个实例独立拥有的数据(尤其是数组、对象、日期等引用类型),必须在构造函数内通过 this 显式赋值,而不是写在原型上。子类通过 super() 或 Parent.call(this) 触发该初始化逻辑,保证每次创建新实例都获得全新副本。
- ✅ 正确:在
constructor中初始化this.items = []、this.config = {...} - ❌ 错误:在
Parent.prototype上定义items: []—— 所有实例共享同一数组 - ⚠️ 注意:即使使用
class,若在类体顶层写items = [](类字段语法),ES2022+ 环境下仍属实例属性,是安全的;但老环境需靠构造函数兜底
只在原型上挂载方法,避免重复创建函数
方法属于行为逻辑,不保存状态,应统一放在原型链上供所有实例复用。这样既节省内存,又天然支持多态调用(如子类重写同名方法)。
- ✅ 推荐:
class Parent { say() { ... } }或Parent.prototype.say = function() { ... } - ❌ 避免:在构造函数里写
this.say = function() { ... }—— 每个实例都新建一份函数 - ? 提示:私有方法可用闭包或
#privateField(仅限 class 内部访问),但注意它不可被子类继承
慎用原型链继承原始实例,优先选择 Object.create(Parent.prototype)
直接用 Child.prototype = new Parent() 会执行一次父构造函数,可能触发副作用(如发请求、改全局状态、初始化冗余数组),且无法传参。现代方案应切断原型连接与构造执行的耦合。
- ✅ 寄生组合式继承:先
Child.prototype = Object.create(Parent.prototype)建立干净原型链,再在子类constructor中调用super()初始化实例属性 - ✅ ES6
class Child extends Parent自动采用此模式,无需手动处理 - ❌ 不要写
Child.prototype = Parent.prototype—— 破坏封装,子类改方法等于改父类
利用闭包或模块作用域封装敏感逻辑
当某些状态或工具函数不应暴露给子类或外部时,不依赖继承机制,而是用立即执行函数(IIFE)、模块导出或类内私有字段隔离。
- ✅ 示例:用
#token私有字段存储认证信息,子类无法访问 - ✅ 示例:将校验逻辑封装在模块内部函数中,只导出经过封装的公共接口
- ? 封装不是继承的附属功能,而是独立的设计层 —— 继承解决“是什么”,封装解决“谁可见”











