我读到每个函数都有自己的堆栈,这意味着当函数结束时,它的变量不再保留在内存中。我还读到对象是通过引用返回的。
考虑这个例子:
function getObject() {
var obj = {name: "someName"};
return obj;
} //At the end the locals should disappear
var newObj = getObject();// Get reference to something that is no longer kept in the stack,
console.log(newObj);//
因此,如果函数返回的值是对不再存在的对象的引用(在堆栈中),我们如何仍然获得正确的值?在C
(语言)中返回指向局部变量的指针是疯了。
首先,快速免责声明:现代JavaScript引擎实际执行的理论和优化过程之间存在差异。
让我们从理论开始:
该对象不在堆栈上,只是包含对象引用的变量。该对象单独存在,并在堆中创建。所以在你的例子中,就在函数返回之前,我们在内存中有这个:
+−−−−−−−−−−−−−−−−−−+ | (object) | +−−−−−−−−−−−−−−−−−−+ [obj:Ref11254]−−−−−>| name: "someName" | +−−−−−−−−−−−−−−−−−−+
变量obj
包含对象的引用(Ref11254
,当然这只是一个概念性数字,我们实际上从未看到过这些)。当函数返回时,返回obj
(Ref11254
)的内容,obj
(变量)与堆栈的其余部分一起消失。但是,只要有东西引用它,在单独存在的对象仍然存在 - 在你的例子中,newObj
变量。
在实践中:
作为优化,现代JavaScript引擎经常在堆栈上分配对象(受特定于引擎的大小限制)。然后,如果在函数终止并清理堆栈后对象将继续存在,则将它们复制到堆中。但是如果对象不能存活(也就是说,没有保留或正在返回它的引用),他们可以让它与堆栈一起清理。 (为什么?因为堆栈分配和清理非常快,而且许多对象仅在函数本地使用。)
值得注意的是,如果函数创建函数,则会出现同样的问题:
function getFunction() {
var x = 0;
return function() {
return x++;
};
}
var f = getFunction();
console.log(f()); // 0
console.log(f()); // 1
console.log(f()); // 2
现在不只是函数getFunction
返回(这是一个对象)幸存下来,但x
也是如此!即使变量“在堆栈上”,当getFunction
返回时堆栈也会被回收。怎么可能?!
答案是,从概念上讲,局部变量不在堆栈中。将它们放在堆栈上是一种优化(常见的)。从概念上讲,局部变量(以及其他一些东西)存储在一个名为LexicalEnvironment对象的对象中,该对象是为调用getFunction
而创建的。从概念上讲,函数getFunction
返回有一个对LexicalEnvironment对象的引用,因此即使在getFunction
返回后它(以及它包含的变量)仍继续存在。 (函数getFunction
返回称为闭包,因为它关闭了创建它的环境。)
当然,在实践中,现代JavaScript引擎并不能真正做到这一点。它们在堆栈上创建变量,可以快速轻松地清理它们。但是如果一个变量需要继续存在(就像我们上面的例子中的x
需要的那样),引擎会将它(以及它需要保留的任何其他变量)放入堆中的容器中并且具有闭包参考那个容器。
更多关于this question's answers和on my anemic little blog的封闭(使用稍微过时的术语)。
因为垃圾收集器比那更聪明!简单地说,如果对象具有活动引用,则不会删除它。当函数结束时(除非你创建了使用它们的异步调用)但是因为它们没有任何活动引用,函数内创建的变量和未返回的变量都会丢失。
例如,参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management