Search code examples
javascriptiife

When using a self-invoking function, why can't I pass references?


I've been using the IIFE pattern for some modules recently and ran into a problem that I can't seem to find the answer to. In my project, I need to pass a couple of global variables in for usage. One of these is a global googletag variable which loads in with a default state and then changes once the external code has loaded in.

However, it doesn't seem to update because the pattern doesn't seem to create a reference but a copy. I've simplified the issue to below.

window.globalLevel = 'First';

var Module = (function(_g){
  function _stuff(){
    return _g;
  }
  return {
      get stuff(){
        return _stuff();
    }
  }
})(window.globalLevel);

// Initial state.
console.log("In Module:", Module.stuff);   // "First"
console.log("In Top:", window.globalLevel) // "First"

// After change.
console.log("--- Changing value ---")
window.globalLevel = 'Second'
console.log("In Module:", Module.stuff);    // "First"
console.log("In Top:", window.globalLevel)  // "Second"

Can I do anything to fix this? What kind of adaptations or considerations should I make? Should I just simply reference the window.globalReference directly in the module? Seems messy but it does seem to work.

JS Fiddle


Solution

  • Your _stuff is currently returning the argument that was initially passed, the _g. So, when you change the global variable with window.globalLevel = 'Second', the argument does not change, so the original argument is what gets echoed back. You can fix it by returning window.globalLevel:

    window.globalLevel = 'First';
    
    var Module = (function(){
      function _stuff(){
        return window.globalLevel;
      }
      return {
          get stuff(){
            return _stuff();
        }
      }
    })(window.globalLevel);
    
    // Initial state.
    console.log("In Module:", Module.stuff);   // "First"
    console.log("In Top:", window.globalLevel) // "First"
    
    // After change.
    console.log("--- Changing value ---")
    window.globalLevel = 'Second'
    console.log("In Module:", Module.stuff);    // "First"
    console.log("In Top:", window.globalLevel)  // "Second"

    If window.globalLevel were an object and not a primitive, both the global variable and the argument would reference the same underlying object in memory, in which case your _g would work:

    window.globalLevel = { value: 'First' };
    
    var Module = (function(_g){
      function _stuff(){
        return _g.value;
      }
      return {
          get stuff(){
            return _stuff();
        }
      }
    })(window.globalLevel);
    
    // Initial state.
    console.log("In Module:", Module.stuff);   // "First"
    console.log("In Top:", window.globalLevel.value) // "First"
    
    // After change.
    console.log("--- Changing value ---")
    window.globalLevel.value = 'Second'
    console.log("In Module:", Module.stuff);    // "First"
    console.log("In Top:", window.globalLevel.value)  // "Second"