-16

Given a loader:

function loader(src, callback, fail) { let s = document.head.appendChild(document.createElement('script')); s.type = "text/javascript"; s.src = src; s.onload = function() { callback() s.onload = null; //useful? s = null; //or maybe this? } s.onerror = fail } 

Does having a line s.onload = null benefit from GC free some memory?

7
  • 1
    What exactly is s (or this) in that case? Usually the whole object can get collected anyway. Commented Jul 4, 2019 at 21:56
  • 2
    delete frag will never do anything – delete deletes properties. Turn on strict mode. Also, I’m pretty sure Bergi never came close to recommending it… Commented Jul 4, 2019 at 23:12
  • Turning on strict mode will tell you that delete frag doesn’t make sense. I don’t think scripts have an onreadystatechange either. Commented Jul 5, 2019 at 0:42
  • Why do you create a fragment wrapper around the script, isn't it the only element that you are going to append? Commented Jul 5, 2019 at 11:50
  • It was an DocumentFragment minimizing reflows, now removed from the post. developer.mozilla.org/en-US/docs/Web/API/DocumentFragment Commented Jul 5, 2019 at 23:02

2 Answers 2

2

Yes, s.onload = null is useful and will garbage collect!

As of 2019, it is not possible to explicitly or programmatically trigger garbage collection in JavaScript. That means it collects when it wants.
Although there is cases where setting to null may do a GC earlier (but not trigg it to do).

As of 2012, all modern browsers ship a mark-and-sweep garbage-collector.
It works from a principle of reachibility:

Every object not reachable from the global context is deleted

Periodically the mark-and-sweep discover and deletes an object when every variable that holded it is returned, reassigned or set to null. Also it is nowadays not needed to recursively set null on content that is not reachable from any variable - it is collected anyway by the mark-and-sweep.

Now to the code in question ...

The s = null is not needed, because the variable cleares anyway when function returns, closure is removed from the call-stack and GC takes care of s.

but there still is a reference to the scripts onload property, as the <script onload> is a child of the document.head in the DOM reachable from the window!

Content of the callback may be reachable, but is out of question here.

What if browsers is smart enough to set s.onload = null internally? We try it out by first comment it away in the first snippet and uncomment it in the second snippet ...

function fetch(src, callback, fail) { let s = document.head.appendChild(document.createElement('script')); s.type = "text/javascript"; s.src = src; s.onload = function() { callback() //s.onload = null; //useful? } s.onerror = fail } fetch("https://stackoverflow.com", () => {console.log("Execute onload");}, () => {console.log("File not found");}) setTimeout(() => { console.log(document.head.lastChild.onload) },1000)

The found file is executed with error, because it is not Javascript.
The onload is executed but not removed. The code show up in the log!
That proves the line with s.onload = null should be used, like this:

function fetch(src, callback, fail) { let s = document.head.appendChild(document.createElement('script')); s.type = "text/javascript"; s.src = src; s.onload = function() { callback() s.onload = null; //useful! } s.onerror = fail } fetch("https://stackoverflow.com", () => {console.log("Execute onload");}, () => {console.log("File not found");}) setTimeout(() => { console.log(document.head.lastChild.onload) },1000)

Sign up to request clarification or add additional context in comments.

3 Comments

"s.onload is not reachable because it is used only one time called directly after the script is executed" - no. Being used and being reachable have nothing to do with each other. You don't want to use it again, right, but the DOM still references it and that's the reason why you should explicitly clear it.
That makes sense to be on the safe side. How to clear it? @Bergi: DOM still references it - In which case does it reference? I hope you make a distinction between the onload-code and the script-code.
You clear it by s.onload = null and/or s.remove(). The document.head references its s child element, and that references the handler function through the .onload property.
1

Yes, it does benefit the GC to set the properties to null. Doing so removes the references from the element (that is contained in the DOM) to the handler function, and given that it probably is the only reference to the function it makes the function eligible for collection. However, unless the function is a closure over variables retaining a large block of memory, this is very unlikely to have a lot of impact.

You might also want to remove the s element from the DOM in the callback, making it garbage-collectable as well.

4 Comments

Note: s.onload = null could be replaced by s = null? I edited my post so you see the scope of s. It is not reassign s. It is a new s for each fetch. Now you see and can be able to refine your answer. Can we now with remove the s more precisely say s = null?
No, s = null is pointless as the scope of s is not retained anyway, the variable gets dropped when the fetch function ends (assuming nothing closes over it). So we ignore that reference to the script element. But to make stuff eligible for garbage collection, we need to remove all references to the values. The most important reference to the script element is that it is contained in the DOM - any code on the page could access the script object through the DOM API and call its onload method. That is the reference that we need to clear.
From what you say it is better to not have any return s, to be sure that the closure is not retained and to be colectable. Or let return value go to void (and closure with it). Yes, s = null is pointless as you say. Maybe any atemts to try help the GC is pointless as I found in my answer?
@PauliSudarshanTerho The return s doesn't matter as long as the calling code doesn't store the value somewhere. And no, that has nothing to do with the closure.