My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Thursday, August 30, 2007

global scope evaluation and DOM investigation

After precedent post I prefer to write another one about global scope evaluation and kentaromiura question:

does your evalScript proposal cause DOM side effects?


Before I post my test code that shows how a medium or big page, about 5000 divs or paragraphs, is not affected by my proposal (about 0.09 delay over 1000 evalScript interactions) I would talk about global scope evaluation.

What does it mean: global scope evaluation?
Every time we use a function to evaluate a string, temporary scope is injected into code evaluation.

While some library (in this case I talk about jQuery) uses stranges (imho) partial (wrong) solutions to remove nested scope from evaluation, I can tell You that everything You need to use a clear scope is to call another function, where this reference is just super object (window if executed inside a browser).

This is an elementary example:

function clearScopeEvaluation(data){
return eval(data);
};

// just call them everywhere ...
(function(a){
clearScopeEvaluation("a = 2");
alert([a, window.a]);
})(1);

// shows 1,2


If You use a setInterval or setTimeout function with delay === 0, You're not solving the problem because these functions are always asyncronous.
This mean that this example code will show 1 in every "delay 0" compatible browser (Opera doesn't accept 0 as delay):

var num = 1;
setTimeout(function(){num = 2}, 0);
alert(num);

// ... or if You prefere ...
var num = 1;
setTimeout("num = 2", 0);
alert(num);

That's why when You use timing or Ajax, callback will be executed at the end of every line of code, only modal events such alert, prompt or others will cause delayed function execution, if time was short enough.


// just click OK before one second or wait more to view 2 value
var num = 1;
setTimeout(function(){num = 2}, 1000);
alert("");
alert(num);

To understand better delayed functions, please read this post that shows a really basic deadlock ... just why JavaScript, if executed inside a browser, is not multi threaded.

John Resig (jQuery author) seems to know it, He implemented these functions (setTimeout and setInterval) inside Rhino (sorry John, I don't find the link) ... but remember that His implementation is based on Java threading and not on JavaScript.

For some reason there's a function inside jQery that I can't understand, called globalEval:

// Evalulates a script in a global context
// Evaluates Async. in Safari 2 :-(
globalEval: function( data ) {
data = jQuery.trim( data );
if ( data ) {
if ( window.execScript )
window.execScript( data );
else if ( jQuery.browser.safari )
// safari doesn't provide a synchronous global eval
window.setTimeout( data, 0 );
else
eval.call( window, data );
}
}

Too many concept errors for a single function ... I hope jQuery team will remove or change them as soon as they can.

Returning on simple "free scope" evaluation, there's always a limit: arguments object or sent variables should be changed by code evaluation.

function gScope(data){
eval("data=arguments=null");
};


Since global scope evaluation should be done exactly using global scope, as script tags do, my evalScript proposal does it quite perfectly and above example will not change, using my function, recieved data or arguments variables.

For people who think that code evaluation is an obsession I can just say that my evalScript proposal was wrote to load entire scripts, as dynamic appendChild(scriptWithExtSource) does, or to evaluate a bit faster big JSON server responses.

In these cases You can't say eval is evil because there aren't different way to do that if You care about speed and dynamic script import.

This is my last sentences about code evaluation, it's always a bad practice but in rare cases, it's a must.

To complete this post, here You can see my benchmark code to test evalScript speed after created a big page (big DOM):

(evalScript = function(e){
var h = evalScript.node,
s = document.createElement("script");
s.type = "text/javascript";
s.text = e;
h.appendChild(s);
h.removeChild(s);
}).node = document.getElementsByTagName("head")[0] || document.getElementsByTagName("*")[0];

onload = function(){

function create(element, content){
document.body.appendChild(
document.createElement(element)
.appendChild(
document.createTextNode(content)
).parentNode
);
};

function randomString(){
var i = Math.ceil(Math.random() * 200),
a = new Array(i);
while(i--)
a[i] = String.fromCharCode(Math.ceil(Math.random() * 40) + 30);
return a.join("");
};


for(var i = 0; i < 5000; i++)
create(i % 2 ? "div" : "p", randomString());

for(var i = 0, j = 0; i < 1000; i++){
startTime = new Date;
evalScript('endTime = new Date - startTime');
j += endTime;
};

alert(j / 1000);

};

Wednesday, August 29, 2007

A better JavaScript code evaluation?

Yesterday Ajaxian published a benchmark using Flex and comparing them with Ajax JSON, Dojo and other way to transfer data between client and server.

Interesting point is not that kind of bench (imho) but one comment wrote by Albert Kühner:


A question about AJAX-JSON: Is there a performance difference if you dynamically create a script tag which loads a generated js file in which all objects are serialized to JavaScript objects (like JSON but without having to eval() the XHR response)?


It seems to be a simple and basic question but He was right because the answer is: yes, a script tag is generally interpreted faster than code evaluation using eval, execScript or Function constructor.

I suppose the reason is quite obvious: eval, as execScript or Function, brings temporary local scope inside evaluated code and this cause a little overhead while a script tag is always interpreted only inside global scope (window or super object).

This should be a limit for evaluation common usage (often wrong) but this was the primitive Ajax implementation to interact with server.

Example:

// specific callback to show product informations
function productInfoCallback(product){
alert(product);
}

// function to send something using GET way (query string)
function sendRequest(queryString, callback){
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "interactor.php?".concat(queryString, "&callback=", callback);
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("*")[0])
.appendChild(s).parentNode.removeChild(s);
};

// generic product id
var productId = 123;

// interaction
sendRequest("productId=".concat(productId), "productInfoCallback");


Now try to image server side code, in this case a really simple PHP script:

if(isset($_GET['productId'], $_GET['callback'])){
$productInfo = 'Web Reflection Blog';
//$productInfo = mysql_query ...
header('Content-Type:text/javascript');
exit($_GET['callback'].'("'.addslashes($productInfo).'")');
}
?>


Well done, this is Ajax without Ajax ... a sort of Asyncronous JavaScript and JavaScript (could we call them Ajaj ? :D )


At this point You should agree that there's no way to inject temporary scope inside response and when You load manually or dynamically a script tag the behaviour is the same.

Is it ok? Is it clear?

Well, next step is to use script tag instead of code evaluation to perform quickly (I'm talking about milliseconds) expecially when We use Ajax to recieve a JSON string.

With JSON We usually don't need temporary scope so why We should "wait" more time to evaluate that string?

This is an elementary proposal, the function evalScript:

(evalScript = function(e){
var h = evalScript.node,
s = document.createElement("script");
s.type = "text/javascript";
s.text = e;
h.appendChild(s);
h.removeChild(s);
}).node = document.getElementsByTagName("head")[0] || document.getElementsByTagName("*")[0];


How to use them? It's really simple:

// basic example
evalScript('alert("Hello World")');


The only thing You need to remember is that scope is not injected inside evaluation:

function badEvalScript(){
var string = "something";
evalScript('var string2 = string + " else"');
};

badEvalScript();
// string is not defined
// ... and string2 will be global (window.string) if string is defined



So how to use evalScript ?
You can use them after one or more Ajax interaction if You're loading a script, instead of XML or (x)HTML or text, to run for example dinamically external resources.

You can use evalScript with JSON interactions too, but You should use a little trick if You don't want to be obtrusive.

The simplest solution is, in fact, this one:

evalScript('JSONResponse = '.concat(xhr.responseText));

... but in this web world full of different libraries this is not a good idea, so why don't use evalScript itself to store temporary results?


// generic Ajax interaction ...
evalScript('evalScript.result = '.concat(xhr.responseText));
var response = evalScript.result;


Since JavaScript is, at least inside browsers, single thread, there's no risk about concurrency and in this way You'll not overwrite anything different from single evalScript function.

If You don't believe this function is usually faster than eval, just try to generate a big and complex JSON notation, using every compatible kind of compatible value, so try 10 or more times to eval them or to use my evalScript proposal.

Of course, this is just for performances maniacs :P


Update
You can use evalScript to solve correctly global scope code evaluation too, sorry John (and jQuery team) but I suppose that eval.call(window) and setTimeout(f,0) aren't a "realistic" solution (first one requires this inside evaluation while second one, I suppose, is not syncronous).


function globalScopeEvaluation(){

var testMe = "private scope";
evalScript('alert(testMe)');

};

var testMe = "global scope";

globalScopeEvaluation(); // global scope


Finally, if You call another function to evaluate something, its scope is just global, You don't need to use eval.call or other strategies, am I wrong?


function eval2(data){eval(data)};
var a = 1;
(function(a){eval2('alert(a)')})(2);
// alert 1 ...

So I probably don't understand eval.call usage and setTimeout with data ... anyone could explain me better what does jQuery do with globalEval method? Thank You :-)

Tuesday, August 28, 2007

SWF Activation using Flash Satay method

Long long IT time ago (2002) A list Apart wrote about a new W3 markup compatible way to show SWF files in a web page using a method called Satay.

This was my favourite one to create valid (x)HTML pages and this method is unobtrusive too, allowing alternative content directly inside object tag without JavaScript requirement.

This method is able to send common informations inside SWF too, using a "sentinel" file or directly full SWF to load.

Since Internet Explorer 6 add a new ActiveX control that requires at least one click before user can use SWF content many developers wrote different libraries to fix this problem, present on IE 7, Opera 9 and probably other browsers.

These libraries are good enough to solve "activation click" problem but these often offer more features we (at least me) need.

This is why I've just uploaded my last tiny library to solve this problem, compatible with IE6, 7, Opera 9, FireFox 1.5+, Safari 2+ and many other browsers.

Its size? Actually exactly 1Kb (1024 bytes) 999 bytes and it seems to work correctly :-)

How to use them? Just include them externally using a script tag.

Demo page

Crazy Source (self packed)