48
votes

Since I've dealt in the past with javascript's funky "object model", I assume there is no such thing as a destructor. My searches were mildly unsuccessful, so you guys are my last hope. How do you execute stuff upon instance destruction?

4
Your use of the pejorative isn't going to get you very far here. There's nothing "funky" about JavaScript's "object model" (which should NOT be in quotes). Just because it's not what you're used to (probably "classical OO"), doesn't mean there's anything wrong with it.Ethan Brown
There is not concept like that in javascript, what do you want to exactly achieve with that?ncubica
@EthanBrown quite some attitude you got there sir. Has it occurred to you that I may not necessarily mean that it's wrong by adding those quotes, rather than it's not quite what we're used to as nearly all other languages which have introduced object model have stuck to?php_nub_qq
@php_nub_qq I suggest you ask a new question relating to that specific problem, which seems like it could get you more valuable insight into both how JS works and how your specific design might be done in a better way.Marcus Stade
@Andrew you can now answer it.php_nub_qq

4 Answers

30
votes

MDN is a nice resource for JS. No, there is nothing like calling a function when an object ceases.

0
votes

As of more recently, this link is of more use to answer this question. The most important part:

As of 2012, all modern browsers ship a mark-and-sweep garbage-collector.

...

Cycles are no longer a problem

In the first example above, after the function call returns, the two objects are no longer referenced by any resource that is reachable from the global object. Consequently, they will be found unreachable by the garbage collector and have their allocated memory reclaimed.

Limitation: Releasing memory manually

There are times when it would be convenient to manually decide when and what memory is released. In order to release the memory of an object, it needs to be made explicitly unreachable.

So as far as cyclic references goes, de[con]structors aren't really needed.


One cool trick I have thought of though, if you have cyclic references and you want easy manual control over deconstruction...

class Container {
  constructor() {
    this.thisRef = [ this ];
    this.containee = new Containee({ containerRef: this.thisRef });
  }

  //Note: deconstructor is not an actual JS thing/keyword.
  deconstructor() {
    //Have to delete `this.thisRef[0]` and not `this.thisRef`, in
    //order to ensure Containee's reference to Container is removed.
    delete this.thisRef[0];
  }

  doSomething() {

  }
}

class Containee {
  constructor({ containerRef }) {
    //Assumption here is, if the Container is destroyed, so will the Containee be
    //destroyed. No need to delete containerRef, no need for a
    //deconstructor function!
    this.containerRef = containerRef;
  }

  someFunc() {
    this.containerRef[0].doSomething();
  }
}

let c = new Container();
...
//No cyclic references!
c.deconstructor();

So here, instead of the Containee class storing a direct reference to the Container instance, it stores a reference to a size 1 array containing the Container reference, which the Container instance itself can then delete itself from. The array, with the reference, is managed by Container.

But again, this isn't really needed, since garbage collection in all modern browsers is mark-and-sweep and can handle cyclic references.

0
votes

In other languages the destructor is handy for implementing the memento pattern. That's actually what lead me to this topic. For example, in a click event it'd be nice to have a generic function that I can pass the event target to that disables the target and then re-enables it when it falls out of scope. Consider a submit button that does something like this:

function async saveMyStuff(e) {
    const sleeper = new nap(e)
    let data = await fetch(...)
    // a bunch more code.
}

class nap {
    constructor(e) {
      this.button = e.currentTarget
      this.button.disabled = true
    }
    destructor() { this.button.enabled = true }

}

This kind of construct would give me a oneliner that handles enabling/disabling all of my buttons when I'm talking to the backend or doing any other processing. I don't have to worry about cleaning up if I return somewhere in the middle or anything like that.

0
votes

FinalizationRegistry might be what you need. It is not a destructor, but it executes a function once the object is garbage collected. In any case, this is what I wish I had find when I first came on here :)