0

How do I keep a function working with local variables from changing the value of a global? It's like I've passed the value by reference without meaning to do so.

https://github.com/upstageleft/Weird-javascript-problem

Global variable keeps changing when source data (value of an element from a matrix array in a closure) is copied to a local variable and then the local variable value is modified.

Cannot wrap my head around what is going on, but problem renders consistently in FF, Safari and Chrome.

Link includes code and dynamic display of variable values with button to repeat. Should be enough, but you can always view source to verify.

EXAMPLE DATA:

In the lists below, the first item reflects the value of a global variable populated by a value copied from a matrix array in a closure by means of a helper function.

The second list shows that the global value has changed when it should not have. The value of the specified cell of the matrix has been copied to a new variable, and that variable has been manipulated, but the result of that manipulation is somehow copied back to both the global variable and the matrix variable!

  • globVal initial value: 1,1 ←(should be constant)
  • globVar's scope: [object Window]
  • myObj: { oSrc: "1,1", oX: 2 }

Before:

  • globVal[0] (global): 1
  • Operation: newVal = myVal[0] * myObj.oX
  • myVal[0] (local): 1
  • newVal (local): 2
  • map.see(1,1) (closure): 1,1

After:

  • globVal[0] (global): 2
  • Operation: myVal[0] = newVal
  • myVal[0] (local): 2
  • newVal (local): 2
  • map.see(1,1) (closure): 2,1

Here is the code performing the operations, along with a helper function to pull values from the matrix and the object containing the data value to be applied in the operations.

CODE BLOCK A

 1 |  function doStuff(){
 2 |     var myVal = mxGet( myObj.oSrc );
 3 |     var newVal = myVal[0] * myObj.oX;
 4 |     myVal[0] = newVal; // weirdness happens here!
 5 | }
 6 |  
 7 | function mxGet( xy ){
 8 |     var x_y = xy.split(',');
 9 |     return map.see( x_y[0], x_y[1] );
10 | }
11 |
12 | myObj = {
13 |     oSrc: '1,1',
14 |     oX: 2
15 | }

Here are the onload function which initializes the global and the closure which stores the matrix.

CODE BLOCK B

 1 | function init(){
 2 |     globVal = mxGet('1,1'); // global declared here
 3 |     that = this;
 4 |     doStuff();
 5 | }
 6 |  
 7 | map = (function(){
 8 |     myHiddenMatrix = [ [ [0,0], [0,1], [0,2] ],
 9 |                         [ [1,0], [1,1], [1,2] ],
10 |                        [ [2,0], [2,1], [2,2] ] ];
11 |     return {
12 |         pin: function( x, y, val ){ myHiddenMatrix[ x ][ y ] = val; },
13 |         see: function( x, y ){ return myHiddenMatrix[ x ][ y ]; }
14 |     }
15 | })();
  • You should include a minimal case that reproduces your issue in the question itself. – apsillers Mar 21 '16 at 18:47
  • It seems you have outsourced data critical for the understanding of your question. Please remember to provide all necessary data (code, configuration data, exception name ...) in **the question itself**. If the link dies or changes your question will lose most if not all of its meaning! – Kyll Mar 21 '16 at 19:49
  • Thanks! I've pulled in the data, but the demo file at [http://upstageleft.net/files/20160321_js_weirdness.html](http://upstageleft.net/files/20160321_js_weirdness.html) still shows it best. – bespoke_code Mar 21 '16 at 20:18
  • Your `myHiddenMatrix` is not as hidden as you think, because you forgot to [declare your variables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var). Use strict mode. – Bergi Mar 21 '16 at 20:57

1 Answers1

1

This is how javascript (and a lot of modern languages) works. Objects are (essentially) passed by reference whether you like it or not.

See more here: Does Javascript pass by reference?

When you're setting your global, you're setting it to the reference of the object returned by mxGet('1,1') in this line:

globVal = mxGet('1,1');

If you want that to be a new object instead, then you actually have to set globVal to a newly created object, like a new Array such as:

var x = mxGet('1,1');
globVal = [ x[0], x[1] ];

This will make globVal stay the same. It's a subtle, but important, difference.

Community
  • 1
  • 1
MattDiamant
  • 8,561
  • 4
  • 37
  • 46