使用new关键字创建对象时,除传递this外如何访问创建作用域变量?
嘿,这个问题问到点子上了!我来帮你拆解清楚——当用new创建对象时,除了把父实例的this传给子构造函数,确实还有其他方式让子方法访问父作用域的变量,同时也得聊聊你说的传this方案的那些坑。
首先先还原一下你提到的场景代码,方便大家理解:
// 你当前的实现方式 function Parent() { this.parent_property = "我是父对象的属性"; // 把父实例的this传给子构造函数 this.child = new Child(this); } function Child(parentInstance) { this.childMethod = function() { // 通过传入的父实例访问属性 console.log(parentInstance.parent_property); } } const parent = new Parent(); parent.child.childMethod(); // 输出"我是父对象的属性"
其他可行的实现方式
方式一:利用闭包特性(最直接的场景化方案)
如果把Child构造函数定义在Parent的内部作用域里,子方法就能直接通过闭包捕获Parent作用域内的变量——不管这个变量是挂在this上的,还是仅在Parent内部的私有变量:
function Parent() { // 哪怕是Parent内部的私有变量也能访问 const parent_property = "我是父作用域的私有属性"; this.child = new Child(); // Child定义在Parent内部,共享Parent的作用域 function Child() { this.childMethod = function() { // 直接访问Parent作用域的变量,不需要传this console.log(parent_property); } } } const parent = new Parent(); parent.child.childMethod(); // 输出"我是父作用域的私有属性"
注意:这种方式下,每次实例化Parent都会创建一个全新的Child构造函数,如果你需要大量创建Parent实例,可能会有轻微的性能开销,但大部分场景下完全够用。
方式二:用WeakMap存储实例关联(解耦+避免内存泄漏)
如果不想让Child和Parent的作用域绑定,又要避免直接传this带来的强引用问题,可以用WeakMap来存储子实例和父实例的关联关系:
// 用WeakMap存子实例到父实例的映射 const parentChildMap = new WeakMap(); function Parent() { this.parent_property = "我是父对象的属性"; this.child = new Child(); // 把子实例作为key,父实例作为value存入WeakMap parentChildMap.set(this.child, this); } function Child() { this.childMethod = function() { // 通过当前子实例拿到对应的父实例 const parentInstance = parentChildMap.get(this); console.log(parentInstance.parent_property); } } const parent = new Parent(); parent.child.childMethod(); // 输出"我是父对象的属性"
这个方案的好处是:WeakMap的key是弱引用,当父实例不再被其他地方引用时,哪怕子实例还存在,垃圾回收机制也能自动回收父实例,不会造成内存泄漏,同时Child构造函数可以单独复用。
方式三:ES6类继承(适合类层级的属性共享)
如果你的场景是类的继承关系,而不是父实例持有子实例,用extends语法可以让子类方法直接通过this访问父类挂载的属性:
class Parent { constructor() { this.parent_property = "我是父类的属性"; } } class Child extends Parent { childMethod() { // 因为继承了Parent,直接通过this访问父类属性 console.log(this.parent_property); } } const child = new Child(); child.childMethod(); // 输出"我是父类的属性"
如果是父实例要创建子实例,也可以结合闭包或者WeakMap来实现,不过继承更适合“子类复用父类属性方法”的场景。
你当前传this方案的弊端和陷阱
强引用引发内存泄漏
如果子实例的生命周期比父实例长(比如子实例被外部变量引用),子实例持有的父实例引用会导致父实例无法被垃圾回收,造成内存浪费:let globalChildRef; { const parent = new Parent(); globalChildRef = parent.child; // 子实例被外部引用 } // 此时parent已经出了作用域,但因为globalChildRef持有child,child持有parent的引用,parent无法被回收耦合度过高
Child构造函数必须依赖父实例才能正常工作,无法单独创建不关联任何父实例的Child实例,灵活性很差——如果后续想复用Child,就得修改构造逻辑。潜在的循环引用问题
父实例持有子实例的引用(this.child = new Child(this)),子实例又持有父实例的引用,形成循环引用。虽然现代JS引擎大多能处理这种情况,但在一些旧环境或者复杂场景下,可能会影响垃圾回收效率。
内容的提问来源于stack exchange,提问作者Dom

