为什么在函数执行结束后,函数创建并返回的对象仍然存在?

问题描述 投票:4回答:2

我读到每个函数都有自己的堆栈,这意味着当函数结束时,它的变量不再保留在内存中。我还读到对象是通过引用返回的。

考虑这个例子:

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 memory-management
2个回答
7
投票

首先,快速免责声明:现代JavaScript引擎实际执行的理论和优化过程之间存在差异。

让我们从理论开始:

该对象不在堆栈上,只是包含对象引用的变量。该对象单独存在,并在堆中创建。所以在你的例子中,就在函数返回之前,我们在内存中有这个:

                    +−−−−−−−−−−−−−−−−−−+
                    |     (object)     |
                    +−−−−−−−−−−−−−−−−−−+
[obj:Ref11254]−−−−−>| name: "someName" |
                    +−−−−−−−−−−−−−−−−−−+

变量obj包含对象的引用(Ref11254,当然这只是一个概念性数字,我们实际上从未看到过这些)。当函数返回时,返回objRef11254)的内容,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 answerson my anemic little blog的封闭(使用稍微过时的术语)。


2
投票

因为垃圾收集器比那更聪明!简单地说,如果对象具有活动引用,则不会删除它。当函数结束时(除非你创建了使用它们的异步调用)但是因为它们没有任何活动引用,函数内创建的变量和未返回的变量都会丢失。

例如,参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management

© www.soinside.com 2019 - 2024. All rights reserved.