Freigeben über


Javascript Execution Context

Alsalam alikom wa ra7mat Allah wa barakatoh

I've faced a strange javascript exception when running a piece of code on Firefox/Safari, while it works fine on IE.

Here is what Firefox 3.0 says:

 uncaught exception: [Exception... "Illegal operation on WrappedNative 
prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" 
location: "JS frame :

After some searching, I found out that the problem is related to how Firefox/Safari handles the execution context for any called function.

Have a look on this code:

    1:  <html> 
    2:  <head> 
    3:  <script language="javascript"> 
    4:  function ExecuteRef(str, arg) { 
    5:      str(arg); 
    6:  } 
    7:  function TestRun(objID){
    8:      var obj = document.getElementById(objID); 
    9:      var funcRef = obj.parentNode.removeChild; 
   10:      ExecuteRef(funcRef, obj); 
   11:  } 
   12:  </script> 
   13:  </head> 
   14:   
   15:  <body onload="javascript:TestRun('delete1');"> 
   16:   
   17:  <div id='delete1'>This should not appear</div> 
   18:  <div>This should alwasy appear</div> 
   19:   
   20:  </body> 
   21:  </html>

What this code is supposed to do is to remove the first div -'delete1'- just when the page load. What actually happens on FF/Safari, is that nothing is deleted, while on IE the first div no longer appears.

We have 2 functions here, TestRun which given an ID for an element, it creates a reference for that object -ln 8- and a reference for the removeChild method -ln 9- , then passes them both to ExecuteRef, which will simply execute that -ln 5- .

Why is that happening?
Is it a problem with FF Javascript Engine AND Safari's one? or a problem with IE's ?

Actually it's not a mistake at all. It's all about how each engine determines the context of function execution. Let's speak in code language:

 var funcRef = obj.parentNode.removeChild;

funcRef is now a function pointer for removeChild.
When you execute funcRef(args), it's translated as funcRef(this, args) in all javascript engine. this way it's easy for your function body to reference member variables of the object it's defined in.

The difference between IE & FF/Safari engines, is that IE preserves the original caller object, in this case the "obj.parentNode".. so that when it translates funcRef(args) to funcRef(this, args) it makes sure that "this" is "obj.parentNode" and your code works fine. On the other hand, FF/Safari don't preserve the original caller, instead they just try to execute the function in the new context. Which in turn means that in funcRef(this, args), this refers to the ExecuteRef method itself, and as there is no function called removeChild defined inside ExecuteRef, the calling just fails giving that weird exception above.

I've explored different solutions for that problem. I'll try to summarize them here:

1. Explicitly declare the parameters of the function pointer:

  1.  var funcRef = function(x) { obj.parentNode.removeChild (x); }
    

This way, jscript engine will have to preserve the calling object -obj.parentNode-

2. If you already know the full parameters that will passed, you can get use of the closure feature JS supports.

 var funcRef = function(){obj.parentNode.removeChild(obj);};ExecuteRef(funcRef);

3. Use Function.call / Function.apply:
this requires that you pass the context to ExecuteRef..

 function ExecuteRef(context, str, arg) {    str.call(context, arg);}function TestRun(objID){    ....
    ExecuteRef(obj.parentNode, funcRef, obj);}
 That's it so far.
 Thanks for reading!

Comments

  • Anonymous
    July 20, 2008
    PingBack from http://wordnew.acne-reveiw.info/?p=11431

  • Anonymous
    July 22, 2008
    i like IE's way of handling it more .. while the FF/Safari 's way seems more dynamic but i never liked dynamic languages.. so IE's way seems reasonable to me :D and congrats on the new blog great first post (Y)