javascriptsettimeoutjsobject

How come setTimeout still calls callback after object deletion


I had a question, and wondered if you called setTimeout from an object, then deleted the object, would the setTimeout callback still be called?

Apparently yes.

var container = {
    timer: { // the object to be deleted
        start: function() {
            console.log('start');
            setTimeout(this.end, 2000);
        },
        end: function() {
            console.log('end');
        },
    },

    timerStart: function() { 
        this.timer.start();
        setTimeout(this.timerDelete, 1000);
    },

    timerDelete: function() {
        console.log(delete this.timer);
        console.log('deleted timer');
    },
};

After calling container.timerStart(); I recieve the following:

> container.timerStart();
  start
< undefined
  true
  deleted timer
  end

Therefore showing that the object container.timer was successfully deleted, but also that container.timer.end was also called after container.timer was deleted. I understand that delete only removes the reference, and once all references of an object are removed it is removed from memory, but does this mean that setTimeout also stores a reference to its callback?

In essence, my questions are:

  1. Is container.timer actually deleted?
  2. Why does the setTimeout callback container.timer.end still run?
  3. How does setTimeout actually work with reference to this behaviour?

Any feedback or reading resources are greatly appreciated. Thanks!


Solution

  • does this mean that setTimeout also stores a reference to its callback?

    Yes, that's exactly what's going on. It's somewhat similar to:

    const obj = {
      fn: () => console.log('fn')
    };
    const fn = obj.fn;
    delete obj.fn;
    fn();

    Is container.timer actually deleted?

    In the sense that container no longer has a timer property, yes, but the timer function still exists, since setTimeout still holds a reference to it.

    Why does the setTimeout callback container.timer.end still run?

    Because it, too, was queued up in setTimeout. The start runs immediately, which does

    setTimeout(this.end, 2000);
    

    so setTimeout saves a reference to the function in this.end and calls it after a couple seconds.

    Something will only be garbage collected once nothing holds a reference to it anymore (except possibly a circular reference). If something is stored in a closure or in a callback, and the callback can still be referenced, it won't be GC'd, at least not until nothing can reference it anymore.