TheLettuceMaster
TheLettuceMaster

Reputation: 15734

Changing local variable in JavaScript affects original global with different name

I have a global declared at top of script:

var g_nutrition_summary = null;

When the user enters the page, I return network data and give this variable a value.

g_nutrition_summary = json.data;

This line is the ONLY assignment of the variable and is never called again (tested with alerts).

I later use that json.data variable to populate a Bar Chart with the plugin Chart.js. The global assignment is for later use.

Underneath the chart, the user can filter certain types of data it displays with a series of checkboxes. So my goal is, to keep an original value of what comes in from the network, and then make a LOCAL COPY of it and alter the COPY (not the global original) and repopulate the chart. Everytime the user checks/unchecks a checkbox, it will call this function and grab the ORIGINAL global (g_nutrition_summary) and re-filter that.

Here is how I do it:

function filter_nutrition_log()
{
    alert("1: " + JSON.stringify(g_nutrition_summary));

    // reassign object to tmp variable
    var tmp_object = g_nutrition_summary;

    var food_array = new Array("Grains", "Vegetables", "Fruits", "Oils");
    var checked_array = new Array();

    // Make an array of all choices that are checked
    $(".input-range-filter").each(function() 
    {        
        var type = $(this).val(); 
        if ($(this).is(':checked')) 
        {            
            checked_array.push(type);            
        }     
    });

    alert("2: " + JSON.stringify(g_nutrition_summary));

    // Loop thru all the 7 choices we chart
    $.each(food_array, function(i, val) 
    {
       // find out if choice is in array of selected checkboxes
       if ($.inArray(val, checked_array) === -1)
       {
            // it's not, so delete it from out tmp obj we 
            // will use to repopulate the chart with
            // (we do not want to overwrite the original array!)
            delete tmp_object["datasets"][val];
       }
    });

    // Resert graph
    alert("3: " + JSON.stringify(g_nutrition_summary));
    getNutritionChart(null, tmp_object, null, false);
}

Somehow, between alert "1" and alert "2". The global gets changed. Then when the user clicks a checkbox again and it calls this function, the very first alert shows that the original, global object contains the altered data to the tmp_object variable.

As you can see, I call a third party function I have created when this happens originally. Doing a search for the global there is absolutely nowhere else it is used in the instances described above.

Am I not understanding something about JavaScript variable scope?

Upvotes: 0

Views: 61

Answers (2)

juvian
juvian

Reputation: 16068

Both objects and arrays in javascript are treated as references, so when trying to pass them to functions or to "copy" them, you are just cloning the reference

To have a "real copy", you would need to traverse the object and copy its content to another object. This can be done recursively, but fortunately jquery already comes with a function that does this: $.extend

So the solution would be:

var tmp_object = $.extend({},g_nutrition_summary);

If you have a nested object, you need to set an extra parameter:

var tmp_object = $.extend(true,{},g_nutrition_summary); // now it will do it recursively

For arrays, an easy way to make a "real copy" is, as @Branden Keck pointed out,

var arrCopy = arrOriginal.slice(0)

More on jquery extend: https://api.jquery.com/jquery.extend/

Upvotes: 2

Branden Keck
Branden Keck

Reputation: 572

Going along with juvian's comment. To create the new array as somewhat of a "copy" and not just a reference, use:

var tmp_object= g_nutrition_summary.slice(0);

However, .slice() is only works for arrays and will not work on JSON, so to used this method you would have to create an array from the JSON

Another method that I found (although not the cleanest) suggested creating a string from the JSON and re-parsing it:

var tmp_object= JSON.parse(JSON.stringify(g_nutrition_summary));

Upvotes: 1

Related Questions