Reputation: 18340
I have function source stored in a string eg.:
var source="(function(){return Math.sin(x);})";
I want to pass it to eval()
and calculate it's value later:
var f=eval(source);
var v=f();
It works well. However, I want to be able to use functions from Math
without Math.
prefix. So I would be able to do:
var source="(function(){return sin(x);})";
Is there a simple way to achieve this? I already use WebGL, so it doesn't have to be compatible with older browsers.
Additional info:
I'm aware that eval()
is bad. I'm writing function grapher, source
is function entered by the user that is going to be drawn. That's also why want to get rid of Math.
prefixes.
I know it's not going to be pretty but I'm not going to write my own parser. Maybe there is some better way?
Upvotes: 1
Views: 3368
Reputation: 620
Debates about the safety and compatibility of eval
aside, one can avoid the performance penalty in this use case via partial evaluation.
If fstring
is a string containing user input like "x*cos(x)"
,
with (Math) f=eval("(function(x) {return "+fstring+";})");
will create a function f
based on the code in fstring
. f
can be applied quickly to a range of values to generate the graphing calculator output. The performance penalty for doing namespace lookups via with
happens only once, when f
is created.
Upvotes: 1
Reputation: 484
If you're going to use eval()
, maybe you could take the user-entered function and do a few global, case-insensitive replacements:
var source = document.getElementById("someInputBox").value;
source = source.replace(/sin/ig,"Math.sin");
source = nsource.replace(/cos/ig,"Math.cos");
source = source.replace(/sqrt/ig,"Math.sqrt");
source = source.replace(/pi/ig,"Math.PI");
source = source.replace(/^/ig,"Math.PI");
source = source.replace(/([a-z]|\d+(?:\.\d+)?)*\s*\^\s*([a-z]|\d+(?:\.\d+)?)/ig, "Math.pow($1, $2)");
result = eval(source);
IMPORTANT: The Math.pow()
replacement won't work for advanced power expressions, such as 5^(5-3^(2*2))
. You'll need to find a more advanced strategy for those. This website has a solution in PHP
, which could possibly be converted to javascript
.
Upvotes: 2
Reputation: 349142
You can use with
for this purpose:
var source="(function(){return Math.sin(x);})";
with(Math){
var f = eval(source);
var v = f();
}
See also: MDN: with
Upvotes: 3
Reputation: 66465
You can use the ancient, slow, ugly with
keyword to extend the scope:
var source="(function(){return sin(x);})";
with (Math) {
var f=eval(source);
var v=f();
}
See also: https://developer.mozilla.org/en/JavaScript/Reference/Statements/with
Upvotes: 7