2

Although code like this is probably not best practice, I ran into something strange while working the other day. Recreating it as a simple example, I expected the following code to alert 'dog' and then 'cat', but instead it alerted 'dog' twice. By my understanding of JavaScript closure, that seems like very odd behavior to me. Anyone have a good explanation for this?

var printMan = {
    printer: function(animal){
        this.innerPrinter =  this.innerPrinter || function(){
             alert(animal)   
        }
        this.innerPrinter(animal)
    }
}

printMan.printer('dog' )
printMan.printer('cat') 

Here's the Fiddle

Edit ----- Thanks everyone for the great explanations. This is one of the reasons why I love this language - there's a great community here. I thought I knew a great deal about the language, but I was wrong about how this function would work. For anyone reading this, it's probably best to not do something like this example.

3
  • 1
    it's because it cached the result and now this.innerPrinter is pointing to the first ever result and it is not a function anymore. Commented Jul 30, 2014 at 19:37
  • 1
    Perhaps cache is technically not the correct word to describe it, but it does (at least loosely) describes the process... Commented Jul 30, 2014 at 19:39
  • 1
    That is a questionable "inner function" because it adds itself to this as a side-effect. That is, it only exists (on printMan) after printMan.printer() and is never used externally and captures the first (and then stale) variable. Commented Jul 30, 2014 at 19:44

3 Answers 3

4

The function you assign to this.innerPrinter does not have any arguments in its definition:

function(){
    alert(animal)   
}

So when you call it with this.innerPrinter(animal), it ignores the argument and gets animal from the wider scope (which is printer: function(animal){).

The first time you call printer, the object does not have a innerPrinter value, so you assign a new function to it.

This function is declared within the scope of the first call to printer, so animal is 'dog'. It alerts 'dog'.

The second time you call the function, this.innerPrinter already has a value, so this.innerPrinter || function(){ evaluates as the function from the first call. It therefore alerts 'dog' because that is the value of animal for the first function generated from the function expression.

Sign up to request clarification or add additional context in comments.

1 Comment

Great explanation. Recreating this, I made a mistake calling the inner print with an argument at all, but it would have been the same result. Truly appreciate the explanation. I thought I knew most about JavaScript but this was still a learning experience.
3

It is in fact perfectly normal that you are getting two dogs. You forgot the parameter in the inner function. It should look like this:

var printMan = {
    printer: function(animal){
        this.innerPrinter =  this.innerPrinter || function(animal){
             alert(animal)   
        }
        this.innerPrinter(animal)
    }
}

printMan.printer('dog' )
printMan.printer('cat') 

Let me try to explain. The first time you go trough the printer function, the innerPrinter gets constructed, and the variable is hard coded in there from it's parent scope. The second time you call the printer, the innerPrinter you made in the previous cycle is used, with your hard coded dog in there. By passing the parameter it is not hard coded and you will get the expected cat on the second run. Changing the variable names may make it a bit clearer:

  printer: function(animal){
        this.innerPrinter =  this.innerPrinter || function(innerAnimal){
             alert(innerAnimal)   
        }
        this.innerPrinter(animal)
    }

Hope this makes sense. Feel free to ask if you want me to explain further.

1 Comment

it might be a little clearer if the argument in alert(animal) were renamed to something else so there is no variable shadowing
2

You need to pass the animal variable as a parameter to the innerPrinter to execute differently. Like:

var printMan = {
    printer: function (animal) {
        this.innerPrinter = this.innerPrinter || function (animal) { 
            alert(animal);
        }
        this.innerPrinter(animal);
    }
}

printMan.printer('dog');
printMan.printer('cat');

1 Comment

Looks like @PeterVR beat me by a few seconds - it is exactly what he said ;)

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.