Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the Arguments object leak?

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?

like image 972
Bergi Avatar asked Nov 22 '12 15:11

Bergi


1 Answers

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

like image 66
raina77ow Avatar answered Nov 10 '22 13:11

raina77ow