79

Is there destructor in TypeScript? If not, how can I delete an object? I tried destructor() and ~ClassName() but it didn't work.

3 Answers 3

92

JavaScript uses garbage collection to automatically delete objects when they are no longer referenced. There is no concept of destructors or finalizers.

You can't observe when an object is deleted by the garbage collector, nor is it predictable.

9
  • 7
    Garbage collection may be prevented if a timer, callback or event listener is somewhere still attached to the object to be deleted. You can also get errors if the callback/listener still exists but the object does not. If you want to be absolutely sure you could write your own destructor method and call that before deleting the object.
    – Kokodoko
    Commented Nov 28, 2015 at 12:26
  • 40
    This is not a correct answer. Gábor Lupák most like knows that there is a garbage collection happening there. Even with a garbage collector, you still need sometimes to release some long living references at specific points during the app's execution so that the references resource could be eligible for garbage collection or at least so that there is no memory leak. My suggestion is that you create an interface (example: interface Releasable {} and define a method called release() and maybe a flag called released that you can use to prevent running release() more than once.
    – asiby
    Commented Nov 2, 2016 at 21:10
  • 11
    @asiby - that would be an appropriate workaround, but that solution is not a destructor. This answer is correct - Javascript has no destructors.
    – Tom Mettam
    Commented Dec 12, 2018 at 12:45
  • 5
    Lastly, destructors require no relationship to the GC to work. Strictly speaking, destructors are called when an object's lifetime ends, not when an object is collected (which has to be AFTER the destructor is called, anyway). Therefore, we don't need to sync the destructor invocation with the GC's actions. Actually, a destructor can be fired at ANY point before collection. If a language's semantics support static lifetimes, the compiler can synthesize an obj.destruct() at the point just before the object's lifetime expires (e.g. when it leaves program scope). Commented Jul 21, 2019 at 21:08
  • 1
    You are incorrect. The only way to allow garbage collection if you are listening for events or are listened to for events is to unsubscribe the event listeners. The method of doing this is by calling a destructor method, which sometimes calls other destructor methods of other referenced class instances, if appropiate, also for the reason of stopping event listeners. Also, at least in NodeJS, observing GC actions is now possible.
    – oxygen
    Commented Dec 18, 2020 at 15:05
17

As of ES2021, finalizers were added to the specification.

To use that feature, you create a FinalizationRegistry, which notifies you when any of the associated objects get garbage collected.

You can use it like that:

const reg = new FinalizationRegistry((id: number) => {
  console.log(`Test #${id} has been garbage collected`);
});

class Test{
  id: number;
  constructor(id: number){
    this.id = id;
    reg.register(this, this.id);
    //                 ^^^^^^^--- This is the "testament", whatever value, which will be passed to the finalization callback
  }
}

{
  const test1 = new Test(1);
  const test2 = new Test(2);
}

Note that when the callback is called, the object had already been garbage collected; only its "testament" (or as MDN puts it, less dramatically, the "held value") is given to the finalizer.

If you need access to some properties of the object in the finalizer, you can store them inside the testament, which, in this case, can (although not necessarily will) be garbage collected just after the original object:

interface TestTestament{
  id: number,
  intervalid: ReturnType<typeof setInterval>
}

const reg = new FinalizationRegistry((testament: TestTestament) => {
  console.log(`Test #${testament.id} has been garbage collected`);
  clearInterval(testament.intervalid);
});

class Test{
  private testament: TestTestament;
  constructor(id: number){
    this.testament = {
      id,
      intervalid: setInterval(() => {
        console.log(`Test interval #${id}`);
      }, 1000)
    };

    reg.register(this, this.testament);
  }
}

{
  const test1 = new Test(1);
  const test2 = new Test(2);
}

Note that the specification doesn't guarantee when garbage collection happens, so the finalizer may not even be called if the object stays in the memory.

2
  • 1
    Is it right, that this mechanism is optional and not guaranteed to do anything? The Mozilla documentation says: "A conforming JavaScript implementation, even one that does garbage collection, is not required to call cleanup callbacks. When and whether it does so is entirely down to the implementation of the JavaScript engine. When a registered object is reclaimed, any cleanup callbacks for it may be called then, or some time later, or not at all." In this case, it is totally useless when you want to prevent memory leaks of self-managed references. Commented Oct 19, 2022 at 9:17
  • @ChrisoLosoph Unfortunately, that is true. But AFAIK, the callbacks will run in most major browsers.
    – FZs
    Commented Oct 19, 2022 at 14:23
-19

You can actually

    class MyClass {
        constructor(input1, input2){
             this.in1 = input1;
             this.in2 = input2;
         }

    }
    let myObject = {};


    try {
         myObject = {
             classHandler: new MyClass('1','2')
         }
    } catch (e) {
    } finally {
        delete myObject.classHandler
        // garbageCollect
        if (global.gc) {global.gc()}
    }


    
11
  • 2
    Can you elaborate on how this works? My understanding of the delete operator is that it just removes that reference to the object, and garbage collection still happens unpredictably as usual. Commented Apr 25, 2019 at 17:24
  • 1
    "As of 2019, it is not possible to explicitly or programmatically trigger garbage collection in JavaScript." - Mozilla. But this scaffold provides a predictable way the object can be considered garbage. Commented Jul 17, 2019 at 13:00
  • Even as a work-around it's not that simple. Suppose the class sets an interval timer that calls one of its methods. That's just one of the many many things that can prevent the object from being destroyed during garbage collection. There is often a lot of confusion regarding delete, especially among newcomers to JavaScript, and I fear this answer perpetuates that. delete has nothing to do with garbage collection. In practice, it seems that the way to implement predictable resource clean-up is to add a method (e.g. destroy) then call it when the object is no longer needed by the app.
    – jacobq
    Commented Sep 20, 2019 at 19:44
  • 2
    There is a conceptual misunderstanding going on. The concept of destructor is only tangentially related to garbage collection, as in some languages the GC may be the one calling it, but that's not necessarily the case at all and an instance could be disposed or have it's destructor called in a moment before GC but not tied to it at all. Some languages do tie their concept of "destructor" to GC, but in practice implementors of IDisposable have 2 types of destructors, one being that can be used deterministically and other not. But that is just a specific implementation, not the general concept.
    – Trinidad
    Commented Oct 8, 2020 at 1:03
  • 1
    @ThomasThornier A "destructor" does not mean a way of deleting a reference like the JS delete. It means a method defined by a class that is automatically called when the object is garbage collected. Those using the language write the destructor themselves and usually use it to do clean up (close connection, remove temporary file or the like) that should happen when garbage collection happens. Commented Oct 21, 2022 at 15:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.