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?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The weird thing about `arguments` is that the named parameters are actually **aliases** for the elements of the pseudo-array. Try it! If you change `arguments[0]`, then `a` will change too! I don't think it leaks for that reason. – Pointy Nov 22 '12 at 15:43
  • 1
    Really? I thought it was the other way round: `arguments[0]` being an alias for `a` :-) The algorithm which is described in the spec indicates that the indices of `arguments` were setters&getters with a "ParameterMap" and a link to the invocation's environment record - a possible reason to leak imho. That's why I'm also asking about actual implementations... – Bergi Nov 22 '12 at 16:09
  • Well I guess it's kind-of a mystery which is an alias for which :-) What I'm thinking is that because the arguments object basically **is** the set of parameters, it doesn't need a reference to the closure or anything, so other than the memory it uses directly it doesn't "pin" anything else. That's just a hunch, however, and I don't know the real story. – Pointy Nov 22 '12 at 16:22
  • Oh except for the `callee` property; I suppose that could be a problem. – Pointy Nov 22 '12 at 16:23
  • 2
    FWIW, strict mode severs the connection between formal parameters and the arguments object. Changing one doesn't change the other. In other words, each gets its own *copy* of the passed argument. – I Hate Lazy Nov 22 '12 at 18:07
  • `[args, getf] = (function(x) {return [arguments, () => x]})(3); args[0] = 42; getf()` will return 42. Did not expect that. And `[args, getf] = (function(x) {'use strict'; return [arguments, () => x]})(3); args[0] = 42; getf();` returns 3. – Johannes Kuhn Oct 31 '19 at 15:23
  • 1
    Oh, the meta effect… Might this get reopened after 7 years? – Bergi Oct 31 '19 at 18:04
  • @Bergi Yes, it will. – pppery Nov 01 '19 at 01:54

2 Answers2

4

Consider this:

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

It's false, because it's not the same arguments object: it's (for each invokation of x) a newly constructed object that has the values of all the params stored within. Yet it has properties of 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; }

Yet there's more to this. Obviously, objects sent into function as its params will not be collected by GC if arguments are returned:

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

This is expected: after all, you can get the similar result by declaring some local object inside the function, assigning the value of the function's first parameter as its object '0' property, then returning this object. In both case the referred object will still be "in use", so no big deal, I suppose.

But what about this?

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);

Output:

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

As you see, if you return arguments object and assign it to some variable, inside the function its callee.argument property points to the same set of data as arguments itself; that's, again, expected. But outside the function variable.callee.arguments is equal to null (not undefined).

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • `variable.callee.arguments` is not the same as `arguments`, it's a non-standard construct. See my answer to [this question](http://stackoverflow.com/q/11939736/825789). – bfavaretto Nov 23 '12 at 20:15
0

Without having done any research into a particular JavaScript engine this is hard to answer conclusively. I would however argue that the relation between the arguments Object and the context created by example is the same as of any other local variable and its host context.

That is to say, storing the value does not require its context to be stored as well.

One caveat is the arguments.callee property which is a reference to the context (i.e. Function) which a given arguments Object is bound to. This property however does not exist in strict mode and also has been deprecated.

Other than that I think it's safe to assume that returning and storing the arguments Object won't lead to a memory leak.

FK82
  • 4,907
  • 4
  • 29
  • 42
  • `argument.callee` just references the `example` function, doesn't it? I was asking only about other values in the execution context of `example`, like `c`. – Bergi Nov 22 '12 at 19:41
  • Yes that's what it does. I'm not really sure what your point is though. A local variable (like `c`) will not be referenced in the `arguments` `Object`. – FK82 Nov 22 '12 at 22:55
  • Sure, but that's the question. Reading the [spec regarding the `arguments` object](http://es5.github.com/#x10.6), the argument object's internal getters/setters do reference the variable environment record (including `c`). – Bergi Nov 22 '12 at 23:22
  • I don't think that the spec says that. I quote: "The arguments object is created by calling the abstract operation CreateArgumentsObject with (...) *env* the variable environment for the function code,(...)." Pretty sure that *env* refers to the enclosing context not the closed context. – FK82 Nov 23 '12 at 09:11