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

2

6

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?

Blockhouse answered 30/3, 2012 at 11:4 Comment(6)
just out of curiosity, why are you trying to do this?Aloft
Well, the first one actually works for me (Chrome 18)Puckery
Works just fine in Chrome and IE...Wideawake
As a general tip: try things out in the browser's JS console. If you do that, you'll quickly find that JS functions can do exactly what you want; you just have to do it in the right order. First evaluate var r = a(2), and then a('2 * 2 = ' + r)Improbity
It's interesting that it works in Chrome. It shouldn't (see my answer), and doesn't in Firefox or Opera.Tiberius
Funny how I drew my results based on testing in Chrome's console. It alerts 2 then alters 2+2=undefined. Damn... It is pulled away from this question #9937126Blockhouse
T
8

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).

Tiberius answered 30/3, 2012 at 11:8 Comment(0)
R
1

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.

Rubble answered 30/3, 2012 at 11:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.