Search code examples
javascriptnode.jsscopegetter-setterhoisting

JavaScript variable scope/hoisting issues


I'm having some very confusing issues with getting a variable from a different file after it is set locally in the file of origin. I know it has something to do with hoisting but after a while spent researching with no luck I thought it would be worth asking here.

I am trying to get the set element from my file sequencer.js like so:

var nxloader = require('../../helpers/nxloader');                                     

// Load matrix
var matrix = nxloader.load('matrix').getElement(); 
console.log(matrix); 

From the file nxloader.js which sets the variable like so:

var $ = require('jquery');                                                            

/**
 * Variable to hold the element that is loaded.                                       
 */
var element = 'test';                                                                  

/**
 * Constructor                                                                        
 *
 * @returns {nxloader} instance of itself                                             
 */
var nxloader = function () {
    return this;
};

/**
 * Loads the element passed in
 *
 * @param {string} element  The element type to load
 */
nxloader.load = function (element) {

     // Switch on the element type
     switch (element) {
         // Is matrix
         case 'matrix':

            // load matrix
            nx.onload = loadMatrix;

            break;

        default:
            break;
    }

    // Implement fluent interface
    return this;
};

/**
 * Load the matrix element
 */
var loadMatrix = function () {

    // Colours
    nx.colorize("accent", "#ffbb4c");
    nx.colorize("fill", "#1D2632");

    // Specified size
    matrix1.col = 16;
    matrix1.row = 1;
    matrix1.init();
    matrix1.resize($(".step-sequencer-container").width(), $(".step-sequencer-container").height());

    // Set the element
    setElement(matrix1);

    // Implement fluent interface
    return this;
}

/**
 * Get the element
 * @return {nxelement} element
 */
nxloader.getElement = function () {
     return element;
}

/**
 * Local setter
 */
function setElement (elementToSet) {
     element = elementToSet;
};

module.exports = nxloader;

As some might guess, when the console.log(matrix); in the sequencer.js file returns 'test' rather the element I'm hoping for. I know it has something to do with hoisting in JavaScript but I am confused how to solve it. I wanted a local setter so that it can't be set from other files. I wish the element to be set to the global element variable once the element has been loaded so that the other file can retrieve this element and use it as it wishes.

I'm using the NexusUI library that has UI elements that are specific to web-audio programming.

Any help will be greatly appreciated, I think it is a very simple and annoying problem!

EDIT: Put the actual code in the method loadMatrix for clarity. EDIT-2: Included link to NexusUI library


Solution

  • The value is being set properly, but the onload function is being called after your console.log runs. So, the value has not been set at the time of logging. You can prove this by putting the log in a setTimeout function.

    setTimeout(function { 
        console.log(matrix); 
    }, 0);
    

    This pushes the log to the end of the call stack and after the onchange function is called by the library meaning your matrix is ready.

    Depending on how you want to structure your application, you can use a callback or a Promise to deal with asynchronous issues like this. Or you can simply move your console.log to the loadMatrix function where you know you can safely access the element.