I want to make an ajax call that will return a json object. One of this JSON object's properties will be the string of a function to be executed in the client. I realise this can easily be solved by using eval, but seeing the many disadvantages of eval, I'd rather avoid it. My question is:
Can I in some way return from the server some js code and execute it without resorting to eval?
As requested, here's some example code:
Server (Node.js):
var testFunc = function() {
alert('h1');
};
app.get('/testPack', function(req, res) {
var template = jade.render('h1 hi');
res.send({
template : template,
entity : testFunc.toString(),
data : {
id: "OMG I love this"
}
});
});
Client:
$(document).ready(function() {
$.ajax({
url: '/testPack',
success: function(data) {
$('body').append($(data.template))
alert(data.data.id);
var entity = eval(data.entity);
entity();
}
})
})
Of course, the returned function called entity wouldn't do such a silly thing, it would expose an API of the returned widget.
Just to clarify, I'd like to avoid having to make a separate call for the javascript itself. I'd rather bundle it with the template and data to render.
Easiest way to do that, is not to call a server through an ajax, but instead to create a new script tag on the page with the url pointing to a RESTful web-service that would output pure JavaScript (not JSON). That way your output will be evaluated by the browser directly without the use of eval.
To expand a little on my answer: To get around the problems of running script in the global context you could do some tricks. For example, when you are adding script tag to the head, you can bind onload event (or rather fake onload event, since IE doesn't support onload on the script tag) to it, and if your response from the server will be always wrapped in the the function with a known name, you could apply that function from within your object. Example code below (this is just an example though):
function test ()
{
this.init = function ()
{
var script = document.createElement("script");
script.type = "text/javascript";
script.language = "javascript";
script.src = "test.js";
var me = this;
window.callMe = function () { me.scriptReady(me); };
var head = document.getElementsByTagName("head")[0];
head.appendChild(script);
};
this.scriptReady = function (object)
{
serverResponse.call(object);
};
this.name = "From inside the object";
this.init();
}
var t=new test();
The server response should look something like this:
function serverResponse()
{
alert(this.name);
}
window.callMe();
In this case, everything inside serverResponse() will use your object as "this". Now if you modify your server response in this way:
function serverResponse()
{
this.serverJSONString = { "testVar1": "1", "testVar2": 2 };
function Test()
{
alert("From the server");
}
Test();
}
window.callMe();
You can have multiple things being returned from the server and with just one response. If you don't like just setting variables, then create a function in your main object to handle JSON string that you can supply by calling this function from your response.
As you can see, it's all doable, it really doesn't look pretty, but then again, what you are trying to do is not pretty to begin with.
P.S. Just inserting a string inside tag will not work for IE, it will not allow you to do that. If you don't have to support IE, then you could get away with just inserting server response inside a newly created script tag and be done with it.
P.P.S. Please don't use this code as is, cause I didn't spend too much time writting it. It's ugly as hell, but was just ment as an example:-)
No, you can't do this by definition, because JavaScript functions are not valid JSON. See the spec here:
If you're returning a string, then that's what it is: just a string. You can't evaluate it without eval. You can call whatever else you're returning whatever you want, but please don't call it JSON.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With