10

Assuming I had this sloppy-mode function, which (for some odd reason) returns its arguments object to the caller:

function example(a, b/* ...*/) {
    var c = // some processing
    return arguments;
}

Does storing the result of an invocation (var d=example();) prevent the variable environment of example (containing a, b, c etc) from being garbage-collected? The internal setters and getters of the Arguments object might still refer to it, just like a function returned from a closure does. Demo:

function example(a, b) {
  var c = Array(1000).fill(0); // some large object
  return {
    args: arguments,
    set: function(x) { a = x; },
    get: function() { return a; }
  };
}
var d = example('init');
console.log(d.get());
d.args[0] = 'arguments update'; // assigns the `a` variable
console.log(d.get());
d.set('variable update');
console.log(d.args); // reads the `a` variable

I know there is hardly a use case (and passing around Arguments objects is considered bad practice, most likely because of their similarity to arrays), but this is more a theoretical question. How do different EcmaScript implementations handle this? Is it implemented close to the spec?

I would expect c to be garbage-collected like with a normal closure and not to be leaked, but what about b? What would happen if I delete the properties of the arguments object?

4

2 回答 2

4

考虑一下:

var x = function() {
  return arguments;
}
console.log( x() === x() );

这是错误的,因为它不是同一个arguments对象:它(对于 的每次调用x)是一个新构造的对象,其中存储了所有参数的。然而它具有以下特性arguments

var y = x([]);
console.log(y instanceof Object); // true
console.log(y instanceof Array);  // false
console.log(y.length); // 1
console.log(y.callee + '');       // function() { return arguments; }

然而,还有更多。arguments显然,如果返回,作为参数发送到函数的对象将不会被 GC 收集:

var z = x({some: 'value'});
console.log(z[0]); // {some:'value'}

这是意料之中的:毕竟,您可以通过在函数内部声明一些本地对象,将函数的第一个参数的值分配为其对象'0'属性,然后返回该对象来获得类似的结果。在这两种情况下,引用的对象仍将“使用中”,所以我想没什么大不了的。

但是这个呢?

var globalArgs;
var returnArguments = function() {
  var localArgs = arguments;
  console.log('Local arguments: ');
  console.log(localArgs.callee.arguments); 
  if (globalArgs) { // not the first run
    console.log('Global arguments inside function: ');   
    console.log(globalArgs.callee.arguments); 
  }
  return arguments;
}
globalArgs = returnArguments('foo');
console.log('Global arguments outside function #1: ');   
console.log(globalArgs.callee.arguments);
globalArgs = returnArguments('bar');
console.log('Global arguments outside function #2: ');   
console.log(globalArgs.callee.arguments);

输出:

Local arguments: ["foo"]
Global arguments outside function #1: null
Local arguments: ["bar"]
Global arguments inside function: ["bar"]
Global arguments outside function #2: null

如您所见,如果您返回arguments对象并将其分配给某个变量,则在函数内部,其callee.argument属性指向与其自身相同的数据集arguments;这又是预期的。但函数外variable.callee.arguments等于null(不是undefined)。

于 2012-11-22T15:53:32.660 回答
0

在没有对特定的 JavaScript 引擎进行任何研究的情况下,很难得出结论性的答案。然而,我认为arguments Object和创建的上下文之间的关系example与任何其他局部变量及其宿主上下文之间的关系相同。

也就是说,存储值不需要也存储它的上下文。

一个警告是属性,它是对给定绑定到arguments.callee的上下文(即Function)的引用。arguments Object但是,此属性在严格模式下不存在,并且已被弃用

除此之外,我认为可以安全地假设返回和存储arguments Object不会导致内存泄漏。

于 2012-11-22T19:36:06.163 回答