-2

As far as I understood, for loops in javascript do not create seperate closures. So can someone explain to me why in the following code the array dogs is not filled two references to the same objects? Namely {name: 'lunski', species: 'dog'}

var animals = [
    {name: 'fluffy', species: 'dog'},
    {name: 'miauw', species: 'cat'}, 
    {name: 'lunski', species: 'dog'}
];

var dogs = [];

for(var i = 0, e = animals.length; i<e; i++){
    if ( animals[i].species === 'dog' ){
        dogs.push(animals[i]);
    }
};

I would think that both the array elements in dogs[] would hold a reference to animals[2], but in fact the array dogs will hold both the fluffy object and lunski object.

EDIT: So, it almost looks as if a closure is being created (even though I understand that this is not the case). But the question remains, why is dogs[] filled with distinctive objects?

sjbuysse
  • 3,872
  • 7
  • 25
  • 37
  • 1
    Why do you think closures are involved here? There are no closures in this code. There aren't even any functions in this code. – user2357112 Oct 03 '16 at 06:14
  • 1
    pushing `animals[i]` will not create a reference instead the value is pushed – Naeem Shaikh Oct 03 '16 at 06:14
  • No closure is involved here as there is no function involved .The length of dogs is 2. – Debabrata Mohanta Oct 03 '16 at 06:16
  • 1
    your code says "if species is dog, add animal to list of dogs". That's precisely what it does. Both fluffy and lunski are dogs. – miraculixx Oct 03 '16 at 06:17
  • To the people saying there are no closures involved (and are being harsh with their downvotes), that's what I suspected as well, hence the topic... I was surprised to see the values correct, but I learned now that `.push()` passed `animals[i]` as values and not references. – sjbuysse Oct 03 '16 at 06:41
  • 1
    *I would think that both array elements would hold a reference to animals[i] with i being 2 in this case.* Why would you think that? You are pushing `animals[i]`, so it pushes `animals[i]`, which depends on the **current** value of `i`, not the final value. –  Oct 03 '16 at 07:43
  • Sounds like you're overly confused about loops and closures and are trying to see problems where there are none. – deceze Oct 03 '16 at 08:54
  • `for (...) setTimeout(() => alert(i))` ← *That's* a closure and an example of an easily unexpected result, because it involves asynchronous execution and a function. None of that is an issue in your code. – deceze Oct 03 '16 at 08:56

1 Answers1

2

You are mixing up concepts. Leaving closures out of this (it is something else entirely), let's look at the question you asked.

why in the following code the array dogs is not filled two the same objects {name: 'lunski', species: 'dog'}

You are pushing two distinct objects (animals[0] and animals[2)) into the dogs array - they are not references to the animals object itself (arrays are objects) but to the objects contained in the individual animals elements.

Because this is not a closure The variable i is not fixed to the last value it is set to.

UPDATE

I believe I finally understand your intended question - here is an answer showing how an anonymous function with closure and an anonymous function without closure works with your array (see this fiddle to test).

The code / fiddle creates 4 inputs. The first and third are without closure and the second and fourth are with closure. The inputs are meant to be clicked on (they have a click listener) and they each log animals[i] to the console. See the comments in the example below to explain the values logged.

var animals = [
    {name: 'fluffy', species: 'dog'}, 
  {name: 'miauw', species: 'cat'},
  {name: 'lunski', species: 'dog'}
  ];

var dogs = [];
for (var i = 0; i < animals.length; i++) {
  if (animals[i].species === 'dog') {
    // the first and third elements created in the loop have no closure
    // the variable i will be set to 2 because it has reached the end of its loop - it's value was not bound with function
    // therefore when you click on the input labeled "fluffy (no closure)" or lunski (no closure) they will log "undefined" to the console since animals[2] is undefined.
        e = document.createElement("input");
        e.value = animals[i].name + " (no closure)" ;
        e.addEventListener('click', function() {
          console.log(animals[i])}
        )
    document.body.appendChild(e);


    // the second and fourth elements created in the loop have closure
    // because the function is enclosed within a function
    // the variable i will be valid and set to what it is when the funtion runs 
    // Note: The exclamation point causes the outer function to be executed immediately - creating the event listener
    // therefore when you click on the input labeled "fluffy (closure)" or lunski (closure)
    // you will see the whole object in the console
    e = document.createElement("input");
    e.value = animals[i].name + " (with closure)" ;;
    !function(animals, i){
        e.addEventListener('click', function() {
          console.log(animals[i])}
        )
     }(animals, i)
    document.body.appendChild(e);
  }
};

I hope this helps. If you are interested in seeing more examples of closures, look at this SO post

Community
  • 1
  • 1
mseifert
  • 5,390
  • 9
  • 38
  • 100
  • Can you come up with some example? – suyesh Oct 03 '16 at 06:27
  • @suyesh - Example for what? closure? – mseifert Oct 03 '16 at 06:29
  • @suyesh - If you are interested in learning about closures, see the bottom of my post - I have put in a link. – mseifert Oct 03 '16 at 06:36
  • Ah, it passes a value, not a reference. That answers my question! Then indeed it will not create a closure, which makes sense – sjbuysse Oct 03 '16 at 06:39
  • @sjbuysse - Even if you assigned an object to an element and were therefore sharing a reference to that object, this would not be a closure. See the referenced SO post for more info on closures. – mseifert Oct 03 '16 at 06:46
  • *You are pushing a string into the `dogs` array.* No, he is not. He is pushing an animal object into the `dogs` array. –  Oct 03 '16 at 07:41
  • @mseifert , since we're pushing objects (and not strings) into the `dogs` array, shouldn't they be passed by reference? I understand it's not a closure, but why does it "behave" like one. And with that I mean, why does `animals[i]` refer to the distinctive objects and not just to `animals[2]`? – sjbuysse Oct 03 '16 at 08:35
  • @torazaburo - You are correct. `dogs` will be an array of individual animal objects - which share in common a reference with the `animals` array. I will update my answer to reflect this. Thanks for pointing this out. – mseifert Oct 04 '16 at 00:28
  • @sjbuysse - yes the objects in the dogs array are passed by reference. Any change to an object in the `dogs` array will be reflected in the corresponding animals array element. – mseifert Oct 04 '16 at 00:30
  • @sjbuysse - I've updated the answer with a detailed code example. – mseifert Oct 04 '16 at 03:50
  • @mseifert , not only do I now understand why my example had nothing to do with closures, I now also have a way better understanding of how closures work. Thanks a lot for taking the time to write this out, this has been amazingly helpful. – sjbuysse Oct 04 '16 at 06:24