如何垃圾收集外部 Javascript 负载?

How to Garbage Collect an external Javascript load?

给定一个装载机:

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
}

有一行 s.onload = null 是否受益于 GC 释放一些内存?

是的,将属性设置为 null 确实有利于 GC。这样做会删除元素(包含在 DOM 中)对处理程序函数的引用,并且鉴于它可能是对该函数的唯一引用,它使该函数符合收集条件。但是,除非该函数是对保留大块内存的变量的闭包,否则这不太可能产生很大的影响。

您可能还想从回调中的 DOM 中删除 s 元素,使其也成为 garbage-collectable。

是的,s.onload = null 很有用并且会进行垃圾收集!

As of 2019, it is not possible to explicitly or programmatically trigger garbage collection in JavaScript。这意味着它会在需要时收集。
尽管 there is cases 设置为 null 可能会更早执行 GC(但不会触发它执行)。

截至 2012 年,所有现代浏览器都附带 mark-and-sweep garbage-collector.
它的工作原理是可达性

Every object not reachable from the global context is deleted

周期性地 mark-and-sweep 发现并删除一个对象,当保存它的每个变量被 返回,重新分配或设置为 null.此外,现在不需要对任何变量无法访问的内容递归设置 null - 它无论如何都由 mark-and-sweep.

收集

现在到有问题的代码...

不需要 s = null,因为当函数 returns、闭包从 call-stack 中移除并且 GC 负责处理 s 时变量无论如何都会被清除。

但是仍然有对脚本 onload 属性 的引用,因为 <script onload> 是 DOM 中 document.head 的子节点来自 window!

callback 的内容可能可以访问,但在这里是不可能的。

如果浏览器足够智能,可以在内部设置 s.onload = null 会怎么样?我们先在第一个代码段中将其注释掉,然后在第二个代码段中取消注释...

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://whosebug.com", 
       () => {console.log("Execute onload");}, 
       () => {console.log("File not found");})

setTimeout(() => {
    console.log(document.head.lastChild.onload)
},1000)

找到的文件执行出错,因为它不是Javascript.
onload 已执行但未删除。代码显示在日志中!
证明应该使用s.onload = null,像这样:

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://whosebug.com", 
       () => {console.log("Execute onload");}, 
       () => {console.log("File not found");})

setTimeout(() => {
    console.log(document.head.lastChild.onload)
},1000)