ScopedFunction
allows you to inject scope object into Function
constructor.
The properties of the scope object can be accesed in the function body as if they
were closure variables: ScopedFunction('return s;', { s: 'hello' }) -> 'hello'
.
Build paper-thin DSLs over JavaScript syntax — let your users write math in
standard syntax, exp(10 * cos(x))
, without prepending the nasty Math
builtin, or pass libraries to in-browser JS playgrounds.
There's no runtime performance penalty for functions compiled using ScopedFunction
.
This library is a great foundation for safer and faster eval
or with
, the
infamous optimization busters. ScopedFunction
is tiny: <40 SLOC, or around
500 bytes minified.
// Use whichever you like
const ScopedFunction = require('ScopedFunction');
import ScopedFunction from 'scoped-function';
// You can also drop lib/scoped-function.js into your HTML if you feel like it
// Build your smallest DSL ever:
const trig = ScopedFunction('x', 'return sin(pi * x) + cos(pi * x)', {
sin: Math.sin,
cos: Math.cos,
pi: Math.PI
});
const v = trig(1); // = -1
// Add slight rewriting:
const compileMath = e => ScopedFunction('x', `return (${e});`, Math);
compileMath('atan(exp(x) - 1)')(0); // = 0
// Inject libraries into user code
// Say you're building a JS course with live problems:
const userSolution = `
function ageMode(data) {
if (_.isEmpty(data)) return undefined;
return _.maxBy(
_.values(_.groupBy(data, 'age')),
v => v.length
)[0].age;
}
`;
const _ = require('lodash');
// Note return + call: we can't parse the user-supplied function, but still inject the "_"
const userFn = ScopedFunction(`return (${userSolution});`, { _ })();
const isValid = userFn([{ age: 10 }, { age: 20 }, { age: 10 }]) === 10;
// You can also use ScopedFunction as a constructor:
const f = new ScopedFunction('x', 'return _.min(x)', { _ });
ScopedFunction
shallow-clones the scope object — you can't add, remove, or change
the variables afterwards. However, the objects (as in { hub: {} }
) are shared
by reference. Use this to communicate between the functions, or opt out with
a _.cloneDeep(...)
.
The functions produced mimic the ones that come from Function(...)
:
- respond to
typeof
/instanceof
- have proper
call
/bind
/apply
- accessing
arguments
still works - name is set to
anonymous
as per the spec.
If you find a deviation from the spec, drop an issue.
Both Function
and ScopedFunction
accept non-simple ES6 formal
parameters: ...rest
, { destructuring }
, and default = true
. Use these in
the browser, but remember that your code is compiled as-is, not transpiled,
and would break in browsers with poor ES6 support, like Safari.
Also note that this library does not give you a secure sanbox for untrusted code — use vm2 for that.
$ npm i --save scoped-function
$ yarn add scoped-function
Made by Vladimir Klepov.