JS101: The Function Constructor
by
at 2012-07-09 07:00:00
original http://feedproxy.google.com/~r/dailyjs/~3/AQ_3atrfnhQ/function-2
Last week we took a cursory glance at functions, and this week we’ll look at the Function
object itself.
Creating Functions with the Function
Constructor
Functions can be instantiated with zero or more arguments. The last argument is always the function body:
var sum = new Function('a', 'b', 'return a + b');
sum(1, 1);
// 2
The length
property will return the number of arguments:
sum.length
// 2
This property is not writeable. The function body will have access to arguments
, so supporting variable arguments is still possible:
The behaviour of a function when invoked on a number of arguments other than the number specified by its length property depends on the function.
Other properties include call, apply, and bind. There’s also a toString
method, which will return a string containing the function’s body. It won’t be exactly the same as the source supplied to the Function
constructor.
Scope
To all intents and purposes, functions created this way are indistinguishable from any other function. There is a difference, however, and that lies in the scope binding. The Global Environment is passed as the scope for the new function, which isn’t necessarily intuitive. This isn’t a bug and is covered by the specification, but it’s worth being aware of the behaviour.
For example, this will fail because a
isn’t in scope for the Function
instances:
function container() {
var a = 1
, b = 1
, sum = new Function('return a + b')
, sum2 = new Function('sum()');
sum();
sum2();
}
container();
The amazing thing is, an eval
would have access to a
and b
because entering eval code sets up the lexical and variable environments the usual way.
This difference is useful, because new Function
offers a way to execute arbitrary code without providing access to local (perhaps considered “internal”) variables. jQuery’s JSON parser uses this distinction to parse JSON:
if (rvalidchars.test( data.replace( rvalidescape, "" )
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) {
return ( new Function( "return " + data ) )();
}
In this way we can see eval
and new Function
are related, but not entirely the same. However, some well-known JavaScript professionals strongly warn against using both:
eval
has aliases. Do not use theFunction
constructor. Do not pass strings tosetTimeout
orsetInterval
.
Metaprogramming
The Function
constructor is occasionally used for metaprogramming. It’s used to generate new methods in the Mongoose MongoDB module for Node:
MongooseBuffer.prototype[method] = new Function(
'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' +
'this._markModified();' +
'return ret;'
)
In this case, it would be trivial to refactor out the Function
constructor.
There are more specific cases where the Function
constructor is used for more devious metaprogramming. The Jade template language compiles templates into functions. Dojo also has a few places relating to templates where it’s used as well.
Conclusion
The Function
constructor has some scope behaviour that takes a bit of getting used to, but it’s exploited by a very specific class of libraries. In general, most code doesn’t really need to use new Function
.