Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript inner function prints previous value

Tags:

javascript

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.

like image 342
ncksllvn Avatar asked Mar 16 '26 15:03

ncksllvn


2 Answers

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.

like image 62
Quentin Avatar answered Mar 19 '26 03:03

Quentin


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.

like image 39
Pevara Avatar answered Mar 19 '26 03:03

Pevara



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!