Looking for a specific alternative to eval() in an ajax script

I’ve been reading a lot lately about various tips and tricks for javascript, and a large number of articles and posts that I’ve read all say roughly the same thing: “Don’t use eval()”.

Ok, fair enough. And in most of the (uncommon) occurrences in my scripts where eval() was used, I’ve been able to find an alternative. However, that being said, there’s still one place in my pChat script where I haven’t found an alternative, and that’s in processing the returned string from an ajax request. The crux of the problem is that this return string sometimes comes back as a function call, and sometimes as a property assignment, so it’s difficult to simply apply a single solution without resorting to using eval().

The ajax request function is listed below:


  function ajaxPostEval(ajaxRequest, fName, params, abortIfBlank){
    var response = "";
        //alert("fName = "+fName+"\
params = "+params);
    ajaxRequest.abort();
    ajaxRequest.open("POST", fName, true);
    ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    ajaxRequest.setRequestHeader("Content-length", params.length);
    ajaxRequest.setRequestHeader("Connection", "close");
    ajaxRequest.onreadystatechange=function() {
      if(ajaxRequest.readyState == 4 && ajaxRequest.status == 200) {
        response = ajaxRequest.responseText;
        if (abortIfBlank === false || response != "") {
          //alert("|"+response+"|");
          eval(response);
        }
      }
    }
    ajaxRequest.send(params);
  }

The response text usually comes in the following form:


data.room = 'Attic';updateMainPanel(true);updateLeft();

So my question is: How best might I re-code my function to eliminate the eval()?

Just curious to know this works for you. You just return the static/dynamic test ‘Attic’ in your response, rest are to be done there only.


if (abortIfBlank === false || response != "") {
          data.room = response;updateMainPanel(true);updateLeft();
          //eval(response);
        }

Well, see, that’s not the only form that the response text takes. As earlier stated, sometimes it’s a simple property assignment, other times it’s a function call, and most often it’s both. And the function calls aren’t always the same, either. There are, at present count, 5 different function calls that can possibly be returned, some of which will have unwanted effects if called at the wrong time {e.g. kick(‘Dave’)}, so simply calling all five possible functions with each response is clearly not desired. :slight_smile:

Since the question is asked with the overall goal of increasing performance, I don’t see any gains from introducing more code to separate the response text into two different forms if the resulting code may actually be less efficient than what I’m using now.

I guess what I’m asking is if there is a replacement for eval(“updateLeft()”), because that’s the piece of the puzzle that I’m missing.

Ahh… Ok. Then the way what i am suggesting is not for you i guess until the functions to be called in all cases are not the same. I know it is worth to know the alternatives of eval() method, but to be sure and safe, these days I use jQuery AJAX (for both GET/POST) which makes me easier to handle such things by calling callback functions which might be helpful in your case too.

A way around is to do this:

var scr = document.createElement('script');
scr.type = 'text/javascript';
scr.appendChild(document.createTextNode(response));
document.getElementsByTagName('head')[0].appendChild(scr);

That’ll put whatever “response” is into a <script> tag. When it is appended to the document, the browser runs it.

In a way, it can be seen as a hack, because it’s doing pretty much the same as eval - the browser is blindly parsing some javascript for you, just like eval does. So it’s arguable whether it’s an improvement.

I’d say this is one of the few uses of eval where it’s OK to use it. It’s even what it [I]should[/I] be used for.

Another alternative is to make your response always be in the JSON format. Then you can use JSON functions to parse it. Firefox (and I think Opera and Webkit) can do this natively, but there’s a downloadable function for it at json.org.

I’ll have to look into that, but JSON looks like stereo instructions in Esperanto, to me. :-\

Thanks for the responses, folks. That gives me some things to think about.

JSON sounds like it’s scary but it’s just javascript you already know. So instead of what you’re currently getting, you’d just change it to this:

{
  "data" = {
    "room" = 'Attic';
  }
  "funcs" = function() {
    updateMainPanel(true);
    updateLeft();
  }
}

So when you receive your data, you parse it:


var json = JSON.parse(response);
for (var datum in json.data) data.datum = json.data[datum]; // transfer properties in JSON's data to your local data
json.funcs(); // run the functions

It means you have to think carefully about the structure of your JSON object, and compartmentalise it into static bits of data and functions you want to run.

One of the things I’m currently thinking of doing is to expand my data object into a “pChat” object, complete with methods that will replace many (if not most, or all) of the individual functions that exist at present. Unfortunately, being mostly self-taught in JavaScript, I have some gaps in my knowledge and experience. One of those gaps is JSON, and another is the whole issue of the more advanced uses of objects, and their creation. But since this script that I’m writing has the main role and goal of furthering my education, and closing (or, at least, reducing the size of) the gaps in my education, I’m more than willing to suffer some frustration and uncertainty.

Thank you for the info, Raffles. I’ll look into it. And I’ll post another thread about “objectifying” my script at some point. :smiley:

eval would be my choice, until you can master JSON.

The amount of code that you need to replace it will add up,
with more methods for each new kind of object or format of the response.
In the mean time, you can manually evaluate a string-

Split the responseText on semicolons and parse each expression.

String.prototype.lookup= function(){
	var obj= this.replace(/(^[' "]+|[" ']+$)/g,"");
	var M= obj.match(/(^[a-zA-Z_\\$][\\w\\$]*(\\.[a-zA-Z_\\$][\\w\\$]*)*)/);
	if(M){
		M= M[1].split(".");
		obj= window[M.shift()];
		while(obj && M.length) obj= obj[M.shift()];
	}
	return obj || this;
}
function parseandAssign(txt){
	var str, tem, tem2, M, B= [], A= txt.split(';'),
	Rx1=/^\\s*([^= ]+)(=)([^=]+)$/;
	Rx2=/^\\s*([^(]+)(\\s*\\(\\s*)([^)]+)?\\(\\s*$/;
	while(A.length){
		str= A.shift();
		if(str.indexOf('=')>0){
			M= str.split(/\\s*=/);
			tem= M[0].lookup();
			M[1]=M[1] || '';
			if(tem) tem= M[1].lookup() || '';
			B.push(M);
		}
		else{
			M= str.split(/[)(]/);
			if(M[0]){
				tem= M[0].lookup();
				if(typeof tem== 'function'){
					tem2= M[1]? M[1].split(/\\s*,\\s*/): [];
					L= tem2.length;
					for(var i=0;i<L;i++){
						tem2[i]= tem2[i].lookup();
					}
					tem.apply(tem, tem2);
					B.push(M);
				}				
			}
		}
	}
	return B;
}

var txt= “data.room = ‘Attic’;updateMainPanel(true);updateLeft();”;
alert(parseandAssign(txt));

/*
assignments are made, functions are called in order:
data.room=‘Attic’;
updateMainPanel(true);
updateLeft()
*/

For what it’s worth, I made a mistake in my JSON post - the three “=” should all be “:”.

The best thing to learning to use objects it practice. Just look at them as objects that you can attach things to. Each thing is a property, which can in turn be another object, or something else, like an array, string, number, regular expression or function.