-
-
Save dherman/3190598 to your computer and use it in GitHub Desktop.
(function() { | |
var hasOwnProperty = Object.prototype.hasOwnProperty; | |
var Function = hasOwnProperty.constructor; | |
function isIdentifier(s) { | |
return /[a-zA-Z_$][a-zA-Z_$0-9]*/.test(s); | |
} | |
Closure = function Closure(args, body, env) { | |
var closedVars = [], closedVals = []; | |
for (var key in env) { | |
if (!hasOwnProperty.call(env, key)) | |
continue; | |
if (!isIdentifier(key)) | |
throw new Error("invalid variable name: " + key); | |
closedVars.push(key); | |
closedVals.push(env[key]); | |
} | |
// validate the syntax of the body as a FunctionBody | |
(new Function(body)); | |
// validate the syntax of the formal parameters as FormalParameters | |
Function.apply(null, args); | |
// declare closed variables initialized from an array bound to `this` | |
var closeVars = "var " + closedVars.map(function(x, i) { | |
return x + " = this[" + i + "]"; | |
}).join(",") + ";"; | |
// return the function that closes over the variables | |
var returnClosure = "return function(" + args.join(",") + "){" + body + "};"; | |
closedVals.maker = new Function(closeVars + returnClosure); | |
return closedVals.maker(); | |
}; | |
Closure.prototype = Function.prototype; | |
})(); | |
var f = new Closure([], "return x + y", { x: 1, y: 77 }); | |
console.log(f()); // 78 | |
var g = new Closure(["z"], "return x + y + z", { x: 1, y: 77 }); | |
console.log(g(13)); // 91 |
ghost
commented
Jul 28, 2012
fitzgen: Your solution adds the overhead of another function call and reuses the original function body which means you only get one JITted version of the function for all closed over value sets. The eval method (and new Function method) both create new function bodies for each set of variables, which means that if the types are different you'll get better JITted performance. It does mean it'll use more memory, though...
@fitzgen: They're different operations. Yours is much more statically well-behaved; mine is much more reflective. I don't advocate using reflection except when you know for sure you need it — for example, when writing a JIT such as Shumway. Otherwise I'd advocate using a currying API like yours.
Dave
Cool, thanks for the replies, guys.