Переопределение функции JavaScript

Можно ли переопределить функцию JavaScript из своего собственного тела. Например, могу ли я сделать следующее?

function never_called_again(args) {
  // Do some stuff
  never_called_again = function (new_args) {
    // Do some new stuff
  }
}

Является ли это выше, и имеет ли он правильную семантику? Я не хочу создавать новую глобальную переменную со старым именем функции, потому что я пытаюсь делать такие вещи не в глобальной области, а из разных областей объектов, и мне не нужны столкновения имен, когда я переопределить функцию внутри этих локальных областей.

Ответ 1

Да, вы можете переопределить функцию таким образом.

Запуск этого:

function never_called_again(args) {
    console.log('Func 1', args);

    never_called_again = function (new_args, x) {
        console.log('Func 2', new_args, x);
    }
}

never_called_again('arg A', 'arg B');

never_called_again('arg A', 'arg B');

Уступает:

Func 1 arg A
Func 2 arg A arg B

Ответ 2

Действительно возможно переопределить функцию из своего тела. Этот метод используется в так называемом Lazy Function Definition Pattern.

Он выглядит следующим образом:

// Lazy Function Definition Pattern
var foo = function() {
    var t = new Date();
    foo = function() {
        return t;
    };
    return foo();
}

Эта функция сохраняет дату первого вызова и возвращает эту дату впоследствии.

Если вы сравните с шаблоном модуля, разница в том, что инициализация происходит только при первом вызове, а не при ее определении. Для дорогостоящих инициализаций это может быть огромная польза.

// Conditional Module Pattern
var foo = (function() {
    var t;
    return function() {
        if (t) {
            return t;
        }
        t = new Date();
        return t;
    }
})();

Ответ 3

Полностью можно переопределить функциональный объект во все, что вы хотите, при первом вызове.

Причина, по которой это возможно, заключается в том, что оценки присваивания обрабатываются справа налево. Это означает, что область действия функции захватывается при ее выполнении (в этом случае, когда вы вызываете функцию).

Это на самом деле означает, что во время выполнения объект функции, который выполняется и исходное определение функции, на самом деле представляет собой две разные вещи, что позволяет переопределить исходную функцию как, ну... что-нибудь действительно.

Один из лучших (и самых крутых) способов использования этого - создать функцию, которая по сути своей представляет собой factory. Таким образом, вам не нужно переносить множество определений объектов, которые не будут использоваться.

Пример:

d = function(type){
    switch(type.toUpperCase()){
        case 'A' :
            d = (
                    function(){
                        return {
                            name : 'A',
                            getName : function(){return this.name;}
                        };
                    }
                )();
                break;

        case 'B' :
            d = (
                function(){
                    return {
                        name : 'B',
                        getName : function(){return this.name;}
                    };
                }
            )();
            break;

        default:
            d = (
                function(){
                    return {
                        name : 'DEFAULT',
                        getName : function(){return this.name;}
                    };
                }
            )();
            break;
    }
};

console.dir(d);
d('A');
console.dir(d);
console.log(d.getName());

Ответ 4

Ваш пример в основном такой же, как:

var never_called_again = function(args) {
     //do some stuff
     never_called_again = function (new_args) {
      //do some new stuff
     }
}

Поскольку вы, вероятно, знаете, что это работает:

var a = 1;

function myFunction() {
     //do some stuff
     a = 2;
}

myFunction();

alert(a) //==> 2

ясно, что работает и первый пример.

Если вы действительно хотите это сделать или нет, это более открытый вопрос. Будет ли это сделать код сложнее понять?

Ответ 5

Да, возможно, и он не будет создавать глобальную функцию. Я проверил это в Internet Explorer 6, Firefox, Chrome и Opera. Рассмотрим следующий код:

  <head>
     <title>Never called again</title>
     <style type="text/css">
     </style>

     <script type="text/javascript">

          function hello()   {
             function never_called_again(args) {
                alert("Hello world " + never_called_again);
                //do some stuff
                never_called_again = function (new_args) {
                    //do some new stuff
                    alert("hello " + never_called_again);
                }
             }
             never_called_again();
             never_called_again();

          }

      </script>

  </head>
  <body onload="">

     <button onclick="hello(); never_called_again();">Call</button>
  </body>

Вначале будет напечатан "Hello World {function code}" в первый раз never_called_again, а во второй раз он будет печатать "hello {changed function code}" второй раз.

Но когда он вызывается на кнопке onclick, он выдаст ошибку, заявив, что функция не определена, что четко указывает на то, что функция была переопределена (и не создана глобально).

Ответ 6

Строго говоря, нет, переопределить функцию JavaScript вообще невозможно. Хотя это своего рода вопрос семантики.

Функция может переписывать свою ссылку в определенной области с помощью другой функции, которая служит в качестве ловкости, когда другая функция появляется вместо первой. например.

greet_once = function() {
  console.log('Hello.');
  greet_once = function() { console.log('Can I help you?') };
};

Аналогично, функция может использовать состояние закрытой области для того, чтобы вести себя по-другому во второй раз, когда ее вызвали (очевидно, не "переопределить" ).

В следующем примере показано, почему приведенный выше шаблон не переопределяется, поскольку он зависит от родительской области, которая не может быть разделена с областью вызова.

greet_always = function() {
  greet_once = function() {
    console.log('Hello.')
    greet_once = function() { console.log('Can I help you?') }
  };
  return greet_once()
}()

greet_always()
greet_always()

Функция, вызываемая как greet_always (которая всегда печатает "Hello." ), является той же, которая первоначально была назначена на greet_once.

Ближайшим я считаю, что вы можете переопределить функцию, чтобы переназначить свои методы apply и call, что создает сценарий, в котором myFunction.call() отличается от myFunction(), что является странным, но, возможно, полезным.