Scope in Modular Javascript

Sorry if the answer has already been given, but I did not find a suitable answer here.

I started writing my modular style javascript code recently and I have a question regarding how the module variable area works.

The following code gives me a contradictory answer.

I have a module called Base that declares two strings and an array. It also has a fetchData function that uses the jQuery getJSON shortcut to set these variables with server data. Unfortunately, when I request Base string1 or string2, I get undefined. I understand that this is probably due to the fact that I set two functions in depth for my values ​​(inside the AJAX callback and inside fetchData), and the scope limits this to seeing Base.string1 and Base.string2.

However, when I look at Base.array1 from outside the module, it installs the corresponding data that I pulled from the server, even if it is installed from the same area as the lines.

Here is the code:

namespace.Base = (function(){ var string1, string2, array1 = []; function fetchData(){ $.getJSON('backendScript.php', function(data){ string1 = data.string1; string2 = data.string2; arrayCount = data.arr.length; for(var i = 0; i<arrayCount; i++){ array1[i] = data.arr[i]; } }) } return{ fetchData: fetchData, string1: string1, string2: string2, array1: array1 } })(); 

If i change

 string1 = data.string1; 

to

 namespace.Base.string1 = data.string1; 

It works as I want.

So my question is: why is array1 set correctly when it is set from the same area as strings?

Also, what is the tool for setting module level variables from module functions without having to specify a global path (e.g. namespace.Base.string1)?

+4
source share
3 answers

The problem is that you actually have two different references: the string1 variable in closing the anonymous function that you call to create namespace.Base and namespace.Base.string1 , which is on the object returned from this anonymous function. the variable string1 for the property of the object string1 is one-time, not live. Further modification of the variable string1 will not affect the property of the object. Here is what you want:

 namespace.Base = (function() { var my = { string1: null, string2: null, array1: [], fetchData: function () { $.getJSON('backendScript.php', function(data){ my.string1 = data.string1; my.string2 = data.string2; var arrayCount = data.arr.length; for (var i = 0; i < arrayCount; i++){ my.array1[i] = data.arr[i]; } }); } }; return my; })(); 

Now the local but public members of namespace.Base are in the my object. You can create private variables with var inside an anonymous function or create more general properties by adding them to my .

+3
source

It would be nice to get to know the closure and how they work:

How do JavaScript locks work?

+1
source

Your problem with the "area" is not a sphere problem. The problem is that arrays are pointers to their data, strings are not.

namespace.Base is set to the result (return value) of the anonymous function. - It is defined as an object containing the ref (fetchData) function, two empty lines and an array.

If you later call the fetchData function, then it will change the contents of array1. But it will also create two new rows (from data.string1 and data.string2). The old values ​​string1 and string2 (which are namespace.Base.string1 and namespace.Base.string2) are not changed. Therefore they remain empty (not what you want).

An example of this. Try it in Firebug -

 s1 = "Hi"; s2 = s1; // s2 => "Hi" s1 = "Bye" alert(s2); // *** s2 is still "Hi", it was not changed! // But arrays are different: a1 = ["Hi"]; a2 = a1; a1[0] = "Bye"; alert(a2[0]); // a2[0] is now "Bye" 

Added: Asynch sync error

Also, note that your code is not written that way, as you are not giving the caller any way to know when the Ajax call ended:

 namespace.Base.fetchData(); // starts the Ajax call via getJSON method var a = namespace.Base.array1; // ERROR!! The value of namespace.Base.array1 is // indeterminate since you don't know if the // the Ajax request has completed yet or not! 

It seems you are trying to convert an Ajax asynchronous call (which calls the callback function after receiving a response from the remote server) into a synchronous call that will not be returned until the results are received.

This is a really bad idea. (If you want to know more, ask another question at SO.)

0
source

All Articles