Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Javascript doesn't let a function redefine itself from within itself?

Consider the code:

    window.a = function(x){ 
        var r = x*2; 
        window.a =alert; // redefines itself after first call
        return r;
    }; 
    a('2 * 2 = '+a(2)); // doesn't work. it should've alerted "2 * 2 = 4"

This doesn't work either:

    window.a = function(x){ 
        alert(x); 
        window.a = function(x){ // redefines itself after first call
            var r = x*2; 
            return r;   
        }
    }; 
    a('2 * 2 = '+a(2)); // doesn't work. it should've alerted "2 * 2 = 4"

As neither does this:

    window.a = function(x){ alert(x); window.c = window.a; window.a = window.b; window.b = window.c; };
    window.b = function(x){ var r = x*2; window.c = window.b; window.b = window.a; window.a = window.c; return r; };
    a('2 * 2 = '+a(2)); // doesn't work. 

And basically I've tried all possible ways and neither seem to do the job. Can someone please explain why?

like image 327
Silviu-Marian Avatar asked Mar 30 '12 11:03

Silviu-Marian


2 Answers

You are successfully redefining the function, it's just that the expression calling it has already grabbed the old function reference: The first thing that happens in a call expression is that the thing defining what function to call is evaluated, see Section 11.2.3 of the specification:

11.2.3 Function Calls

The production CallExpression : MemberExpression Arguments is evaluated as follows:

  1. Let ref be the result of evaluating MemberExpression.
  2. Let func be GetValue(ref).
  3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).
  4. If Type(func) is not Object, throw a TypeError exception.
  5. If IsCallable(func) is false, throw a TypeError exception.
  6. If Type(ref) is Reference, then
      a) If IsPropertyReference(ref) is true, then
          i. Let thisValue be GetBase(ref).
      b) Else, the base of ref is an Environment Record
          i. Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
  7. Else, Type(ref) is not Reference.
    a) Let thisValue be undefined.
  8. Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values.

Steps 1 and 2 occur before the function is redefined.

The solution is of course to make things happen in the order you expect (live example | source):

window.a = function(x){ 
    var r = x*2; 
    window.a =alert; // redefines itself after first call
    return r;
}; 
var val = a(2);
a('2 * 2 = '+ val);

Side note: It's interesting that your first example works in Chrome (V8) (it also works in IE6's version of JScript; but then, JScript had lots of issues). It shouldn't work, and doesn't in Firefox (SpiderMonkey), Opera (Carakan), or IE9 (Chakra).

like image 73
T.J. Crowder Avatar answered Nov 19 '22 19:11

T.J. Crowder


JavaScript has a strict left-to-right rule for order of evaluation of operator arguments. I am guessing that this includes the function-call operator, which means that the first a is evaluated before the expression.

like image 20
Marcelo Cantos Avatar answered Nov 19 '22 19:11

Marcelo Cantos