Почему Javascript не позволяет функции переопределять себя изнутри?

Рассмотрим код:

    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"

Это тоже не работает:

    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"

Как и это:

    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. 

И в основном я перепробовал все возможные способы, и ни один из них, похоже, не работает. Может кто-нибудь объяснить, почему?


person Silviu-Marian    schedule 30.03.2012    source источник
comment
просто из любопытства, почему вы пытаетесь это сделать?   -  person maialithar    schedule 30.03.2012
comment
Ну, у меня действительно работает первый (Chrome 18)   -  person Yoshi    schedule 30.03.2012
comment
Отлично работает в Chrome и IE...   -  person Niek H.    schedule 30.03.2012
comment
Общий совет: попробуйте что-нибудь в консоли JS браузера. Если вы сделаете это, вы быстро обнаружите, что функции JS могут делать именно то, что вам нужно; вам просто нужно сделать это в правильном порядке. Сначала оцените var r = a(2), а затем a('2 * 2 = ' + r)   -  person Daniel Earwicker    schedule 30.03.2012
comment
Интересно, что это работает в Chrome. Этого не должно быть (см. мой ответ) и не работает в Firefox или Opera.   -  person T.J. Crowder    schedule 30.03.2012
comment
Забавно, как я получил свои результаты на основе тестирования в консоли Chrome. Он предупреждает 2, затем изменяет 2+2=undefined. Черт... Это оторвано от этого вопроса call/9937382" title="javascript, как использовать функции и менять их назначение после каждого вызова"> stackoverflow.com/questions/9936626/   -  person Silviu-Marian    schedule 30.03.2012


Ответы (2)


Вы выполняете успешное переопределение функции, просто выражение, вызывающее ее, уже захватило старую ссылку на функцию: первое, что происходит в выражении вызова, это то, что вычисляется то, что определяет, какую функцию вызывать, см. раздел 11.2.3 спецификации:

11.2.3 Вызовы функций

Результат CallExpression : MemberExpression Arguments оценивается следующим образом:

  1. Пусть ref будет результатом вычисления MemberExpression.
  2. Пусть func будет GetValue(ref).
  3. Пусть argList будет результатом оценки Аргументов, создающим внутренний список значений аргументов (см. 11.2.4).
  4. Если Type(func) не является Object, генерируется исключение TypeError.
  5. Если IsCallable(func) имеет значение false, генерируется исключение TypeError.
  6. Если Type(ref) имеет значение Reference, то
      a) Если IsPropertyReference(ref) имеет значение true, то
          i. Пусть thisValue будет GetBase(ref).
      b) В противном случае основой ref является запись среды
          i. Пусть thisValue будет результатом вызова конкретного метода ImplicitThisValue GetBase(ref).
  7. В противном случае Type(ref) не является ссылкой.
    а) Пусть thisValue не определено.
  8. Возвращает результат вызова внутреннего метода [[Call]] для func, предоставляя thisValue в качестве значения this и предоставляя список argList в качестве значения аргументов.

Шаги 1 и 2 выполняются до переопределения функции.

Решение, конечно, состоит в том, чтобы все происходило в том порядке, в котором вы ожидаете (живой пример | источник):

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

Примечание: интересно, что ваш первый пример работает в Chrome (V8) (он также работает в версии IE6). JScript; но тогда у JScript было множество проблем). Это не должно работать и не работает в Firefox (SpiderMonkey), Opera (Carakan) или IE9 (Chakra).

person T.J. Crowder    schedule 30.03.2012

JavaScript имеет строгое правило слева направо для порядка оценки аргументов оператора. Я предполагаю, что это включает в себя оператор вызова функции, что означает, что первое a оценивается перед выражением.

person Marcelo Cantos    schedule 30.03.2012