0

I'm confused by this closure behavior. I've read several SO articles (including this one) and MDN's documentation on closures, but haven't seen the explanation for this behavior.

In the code sample below I'm creating a closure containing a variablecache, a function preload that modifies it, and a function report that logs its value. It also attaches references to those to an object that is passed in.

'use strict';

var o = {};

(function(obj) {
    var cache = {'initialized': false};

    function preload(assets, done) {
        console.log('Preloading. Value of cache is', cache);
        cache = {};

        for (var i = 0; i < assets.length; i++) {
            cache[assets[i]] = assets[i];
        }
    }

    function report() {
        console.log('Cache from inside is ', cache);
    }

    function get_cache() {
        return cache;
    }

    obj.cache = cache;
    obj.preload = preload;
    obj.report = report;
} )(o);

// {initialized: false}, as expected
o.report();

// {initialized: false}, as expected
console.log('Cache from outside is ', o.cache);

// I expect this to change cache to {1:1, 2:2, 3:3}
o.preload([1, 2, 3]);

// {1:1, 2:2, 3:3}, as expected
o.report();

// {initialized: false}, NOT as expected. Why?
console.log('Cache from outside is ', o.cache);

My expectation is based on my understanding that when the closure attaches cache to the provided obj, it is assigning a reference to the variable within the closure. But the behavior I'm seeing suggests that obj.cache is getting a copy of the closure's cache.

At which point is a copy of cache created and why?

Community
  • 1
  • 1
Mark
  • 999
  • 9
  • 19
  • Why are you not exposing `get_cache` but the initial value of the `cache` variable? – Bergi Feb 25 '17 at 16:36
  • 1
    Because you're making a copy: `cache = {}`. Note that the only closure is the variable `cache` itself. The variable `obj.cache` is not a closure but is instead a normal reference/pointer assignment like in C or Java or other languages. As such, it makes `obj.cache` point to the same object pointed to by `cache` but not the variable `cache` itself. In other words, `obj.cache = cache` assigns the pointer to cache by value (it makes a copy of the pointer like in most programming languages) – slebetman Feb 25 '17 at 16:58

2 Answers2

2

When you call obj.cache = cache; in the beginning, you are making obj.cache points to the same object that cache is pointing to (This object: {'initialized': false})

Then later when you call preload, you point the variable cache to another, new object: cache = {};. Now the cache variable is pointing to a brand new object, while obj.cache still point to the old object that was created in the beginning.

That explain why all log done on cache inside the closure logs new values, while logging obj.cache still show unchanged values. They're now pointing to 2 different objects, and changing the content of the object that cache is now pointing to have no effect on the original object has obj.cache is pointing to.

AVAVT
  • 7,058
  • 2
  • 21
  • 44
  • Oh! Duh! I didn't even think that `cache = {}` was reassigning cache to a new object, but now that you point it out it's very clear. I was thinking this was just emptying the object that `cache` referred to. This makes perfect sense now and my sanity is restored. Thank you. – Mark Feb 25 '17 at 17:52
0

At the time your IIFE got executed, you have added cache to obj.cache and that was that. obj.cache doesn't hold a reference to the cache variable from your IIFE. But, report and preload functions are using cache variable from your IIFE, even as they are invoked outside of the scope where they have been created. As such, reference to the scope inside your IIFE is being preserved for obj.preload and obj.report functions (this is where closure is being observed), but not for obj.cache.

For more details on various situations how closure is being observed and exercised, please check out this link.

Emin Laletovic
  • 4,084
  • 1
  • 13
  • 22