ES6 const 会影响垃圾回收吗?

Does ES6 const affect garbage collection?

在 Kyle Simpson 的新书名中,You don't know JS: ES6 and beyond,我找到以下片段:

WARNING Assigning an object or array as a constant means that value will not be able to be garbage collected until that constant’s lexical scope goes away, as the reference to the value can never be unset. That may be desirable, but be careful if it’s not your intent!

(摘自:Simpson, Kyle。“你不知道 JS:ES6 及更高版本。”O'Reilly Media, Inc.,2015-06-02。iBooks。 此 material 可能受版权保护。)

据我所知,他没有对此进行扩展,Google 上的 10 分钟没有任何结果。这是真的吗?如果是的话,"the reference to the value can never be unset" 是什么意思 恰好 ?我已经养成了将不会更改的变量声明为 const 的习惯,这在具体的 performance/memory 术语中是否是一个坏习惯?

垃圾收集器 (GC) 的工作方式是当某物没有被引用时 ("cannot be reached"),GC 可以安全地说某物不再被使用并回收它所使用的内存。

能够替换变量的值允许删除对该值的引用。但是,与 var 不同的是,const 不能重新赋值。因此,不能从引用该值中删除该常量。

一个常量,就像一个变量,可以在常量消失时被回收"out of scope",就像一个函数退出时,它内部没有任何东西形成闭包。

WARNING Assigning an object or array as a constant means that value will not be able to be garbage collected until that constant’s lexical scope goes away, as the reference to the value can never be unset. That may be desirable, but be careful if it’s not your intent!

这个提示听起来有点像警告而不是必要的(甚至可能有点愚蠢),并试图从这种情况中找出某种特殊情况。

使用 const 变量声明,您不能为变量分配类似于 ""null 的东西来清除其内容。这确实是内存管理方面的唯一区别。自动垃圾收集完全不受是否声明const的影响。

因此,如果您希望将来能够出于任何原因更改变量的内容(包括手动删除对某些内容的引用以允许某些内容更快地被垃圾收集),那么请不要这样做使用 const。这与使用或不使用 const 的任何其他原因相同。如果您希望能够在将来的任何时间(出于任何原因)更改变量包含的内容,那么请不要使用 const。对于了解 const 的用途的任何人来说,这应该是完全显而易见的。

将垃圾回收作为不使用 const 的特殊情况在我看来很愚蠢。如果您希望能够清除变量的内容,那么这意味着您想要修改变量,所以不要使用 const。是的,在可能陷入持久 scope/closure 的大型数据结构上手动启用垃圾回收是您将来可能想要更改变量的原因之一。但是,这只是数百万个原因之一。所以,我再重复一遍。如果您将来出于任何原因想要更改变量的内容,请不要将其声明为 const.

垃圾收集器本身不会将 const 变量或它指向的内容视为与 varlet 变量有任何不同。当它超出范围并且不再可达时,其内容将符合垃圾收集条件。

const 有很多优点。它允许开发人员声明一些意图,即此变量指向的内容不会被代码更改,并且可能允许运行时进行一些优化,因为它知道变量的内容无法更改。而且,它可以防止恶意代码或意外代码更改该变量的内容。在适当的情况下使用这些都是好东西。一般来说,您应该尽可能多地使用 const


我应该补充一点,即使一些 const 数据仍然可以减小大小并使其大部分内容可用于垃圾收集。例如,如果您在 const 数组中有一个非常大的 100,000 元素对象数组(您可能从某个外部 http 调用接收到):

const bigData = [really large number of objects from some API call];

您仍然可以通过简单地清除数组来大量减少该数据的大小,这可能会使数组中的大量对象符合垃圾回收条件,如果没有其他对象引用它们的话:

bigData.length = 0;

请记住,const 会阻止对变量名的赋值,但不会阻止改变变量指向的内容。

您可以对其他内置集合类型执行相同的操作,例如 map.clear()set.clear() 甚至任何具有减少内存使用方法的自定义 object/class。

不,没有性能影响。此注释指的是通过 "unsetting" 变量帮助垃圾收集器(很少需要)的做法:

{
    let x = makeHeavyObject();
    window.onclick = function() {
        // this *might* close over `x` even when it doesn't need it
    };
    x = null; // so we better clear it
}

如果您已将 x 声明为 const,这显然是不可能的。

变量的生命周期(当它超出范围时)不受此影响。但是如果垃圾收集器搞砸了,常量将始终保存它初始化时使用的值,并防止它被垃圾收集,而普通变量可能不再保存它。

我书中的那个注释指的是这样的情况,在这种情况下,您希望能够在其父作用域的生命周期结束之前手动使值 GC'able:

var cool = (function(){
   var someCoolNumbers = [2,4,6,8,....1E7]; // a big array

   function printCoolNumber(idx) {
      console.log( someCoolNumbers[idx] );
   }

   function allDone() {
      someCoolNumbers = null;
   }

   return {
      printCoolNumber: printCoolNumber,
      allDone: allDone
   };
})();

cool.printCoolNumber( 10 ); // 22
cool.allDone();

这个愚蠢示例中的 allDone() 函数的目的是指出有时您可以决定完成对大型数据结构(数组、对象)的处理,即使周围的 scope/behavior 可能会(通过关闭)无限期地存在于应用程序中。要允许 GC 拾取该数组并回收其内存,您可以使用 someCoolNumbers = null.

取消设置引用

如果你已经声明了 const someCoolNumbers = [...]; 那么你将无法这样做,因此内存将一直使用直到父作用域(通过 cool 上的方法所具有的闭包)消失当 cool 未设置或本身 GCd.

更新

要说清楚,因为这里的一些评论线程中有很多 confusion/argument,这就是我的观点:

const 绝对地、肯定地、不可否认地对 GC 有影响——具体来说,是一个值在较早时间被手动 GCd 的能力。如果该值是通过 const 声明引用的,则无法取消该引用,这意味着您无法获得 GCd earlier 的值。该值只有在范围被拆除时才能成为 GCd。

如果您希望能够在父作用域仍然存在的情况下手动使某个值更早地符合 GC 的条件,您将必须能够取消对该值的引用,而您不能这样做如果你使用 const.

有些人似乎相信我的说法是 const 阻止了任何 GC。那从来不是我的主张。只是它阻止了早期的手动 GC。