20

Is it possible to redefine a JavaScript function from within its own body. For example, could I do the following?

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

Is the above valid and does it have the correct semantics? I don't want to create a new global variable with the old function name, because I'm trying to do this kind of thing not in the global scope, but from various object scopes, and I don't want name clashes when I redefine the function within those local scopes.

4
  • 2
    I have no idea whether you can or not but it doesn't seem like a godo thing to be doing. I obviously don't know what your circumstances are but this sounds like it has a great potential for confusion, hard to trace bugs and hard to read code. I'd think hard about whether this is the right solution or if you want to back up a step and ask the question that led you to this as an answer. Sorry if you have already considered all the options though. You never can tell what somebody has thoguth about already or not... Commented Jul 12, 2010 at 9:31
  • 6
    there is legitimate use for this, and this is a powerful thing. People are afraid of what they don't know, but this doesn't mean they are bad. Commented Jul 12, 2010 at 9:36
  • Note that this technique doesn't actually redefine the function, it changes which function never_called_again refers to. The original function doesn't necessarily disappear, given that (although you don't do so here) it is possible to create another variable that references the original function: put original = never_called_again; before reassigning never_called_again and original() will invoke the original... Commented Nov 15, 2012 at 3:04
  • So the TLDR is that functions can't be redefined, but variables containing functions can be? Commented Jan 8, 2024 at 14:56

6 Answers 6

46

It is indeed possible to redefine a function from its body. The technique is used in the so-called Lazy Function Definition Pattern.

It looks like this:

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

This function stores the Date of the first call, and returns this Date afterwards.

If you compare this with the module pattern, the difference is that the initialization only happens when it's first called, not when it's defined. For costly initializations it can be a huge benefit.

// Conditional Module Pattern
var foo = (function() {
    var t;
    return function() {
        if (t) {
            return t;
        }
        t = new Date();
        return t;
    }
})();
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the link it was pretty interesting and I'm using the pattern in a similar kind of way to avoid various conditional execution paths.
This should be the right answer. Nice to find there was a name coined for that practice.
17

Yes, you can redefine a function that way.

Running This:

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

Yields this:

Func 1 arg A
Func 2 arg A arg B

6 Comments

@galambalazs: I gave you credit and I expanded on aspects you didn't cover to my satisfaction. Additionally, my first answer and main answer had nothing to do with yours and was sufficient.
It's not a community wiki. Global variables has nothing to do with function redefinition, they are only related to the Lazy Func. Definition pattern, which is an example for function redefinition. Everything is explained well in the link btw... But again it was only an example. Your answer was to tight and got no votes that's why you've changed it...
@galambalazs: No, before I changed it, I had as many votes as you (1 each) -- before I upvoted you. I changed my answer to make it more complete, incorporating lessons from the article you cited that you didn't. Furthermore I gave you credit. I've seen other people do this. It's supposed to be about improving answers, not silly snits. By your logic, Peter Michaux should be after you for plagiarism. Anyway, I've rolled back my answer
1. What you're talking about is community wiki, when they may gather the answers into one collection. But this thread is not one of them. 2. Have you've seen other people killing innocents? Do you imitate them? Just because other people do something doesn't mean it's right. You should be clever enough to know that. 3. It's supposed to be about answering a question in the best way you could, not about gathering the best of all answers and edit them into one.
Szia Balázs, it's not a community wiki, but not a pissing contest either. I, the Googler, 99.99% of the users of this site, couldn't care less of what stream of typing or copy-pasting was involved in the procedure that resulted in an answer I find useful (or crap). 1. I, the visitor, will often read each, and will LOL on copy-paste monkeys. 2. I, as a community member, will put my upvote to the better answer -- and if one looks like another, adding no value, I will certainly pick the one with an earlier timestamp. So, no worries; you can see this working on your upvote count. ;)
|
3

Your example is basically the same as:

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

Since you probably know that this works:

var a = 1;

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

myFunction();

alert(a) //==> 2

it's clear that the first example works too.

Whether you actually want to do this or not is a more open question. Will it make the code harder to understand?

Comments

3

It's totally possible to redefine a function object into whatever you want upon the first call.

The reason this is possible is because the assignment evaluations is handled from right to left. This means that the scope of the function is captured when you execute it (in this case, when you call the function).

This really means that at the time of execution, the function object that is getting executed and the original function definition are actually two different things, which allows redefinition of the original function as, well... anything really.

One of the best (and coolest) ways to use this is by creating a function that is, in essence, its own factory. In this way you don't have to carry around a lot of object definitions that aren't going to be used.

Example:

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

Comments

1

Yes, it's possible, and it will not create a global function. I verified this in Internet Explorer 6, Firefox, Chrome, and Opera. Consider the following code:

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

This will print "Hello World {function code}" the first time of the never_called_again and the second time it will print "hello {changed function code}" the second time.

But when it's being called on button's onclick, it will throw an error saying that the function is not defined, clearly indicating that the function was re-defined (and not created globally).

Comments

1

Strictly speaking, no, it isn't possible to redefine a JavaScript function at all. Though it's kind of a matter of semantics.

A function may overwrite its reference within a certain scope with another function, which serves as a sleight of hand where another function appears in the place of the first. e.g.

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

Similarly a function may use the state of a closured scope to behave differently the second time its called (obviously not "redefinition").

The following example illustrates why the above pattern isn't redefinition because it depends on the parent scope which may not be shared with the calling scope.

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

The function being called as greet_always (which always prints 'Hello.') is the same one, assigned originally to greet_once.

The closest I believe you can get to redefining a function is to reassign its apply and call methods, which produces a scenario where myFunction.call() is different from myFunction(), which is weird but conceivably useful.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.