Variable Scope in Modular Javascript
Sorry if this has been answered already, but I could not find an appropriate answer on here.
I've started writing my javascript code in a modular style lately and I have a question regarding how module variable scope works.
The following code gives me a conflicting answer.
I开发者_JAVA百科 have a module named Base that declares two strings and an array. It also has a function called fetchData that uses the jQuery getJSON shortcut to set these variables with server data. Unfortunately when I ask for Base's string1 or string2, I get undefined. I understand that this is probably due to the fact that I have it set their values two functions deep (inside the AJAX callback and inside fetchData) and the scope limits it from seeing Base.string1 and Base.string2.
However, when I look at Base.array1 from outside the module, it's set to the appropriate data I pulled from the server, even though it's set from the same scope as the strings.
Here's 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 like I want.
So my question is, why is array1 set correctly when it's set from the same scope as the strings?
Also, what is the remedy for setting module-level variables from within the module's functions without having to give a global path (e.g. namespace.Base.string1)?
The problem is that you actually have two different references, the variable string1
within the closure of the anonymous function that you invoke to create namespace.Base
, and namespace.Base.string1
, which is on the object returned from that anonymous function. Your assignment of the variable string1
to the object property string1
is a one-time set, not a live reference. Further modification of the variable string1
will not affect the object property. Here's 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 object my
. You can create private variables using var
within the anonymous function, or create more public properties by adding them to my
.
I would be a good idea to get familiar with closures and how they work:
How do JavaScript closures work?
Your "scope" issue is not actually a scope issue. The issue is that arrays are pointers to their data, strings are not.
namespace.Base is set to the results (returned value) of the anonymous function. -- It is set to be an object containing a function ref (fetchData), two empty strings 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 strings (from data.string1 and data.string2). The old values of string1 and string2 (which are namespace.Base.string1 and namespace.Base.string2) are not changed. So they are left as empty strings (not what you want).
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 Timing error
Also, note that your code is wrong as written since you're not giving the caller any way to know when the Ajax call has completed:
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!
You appear to be trying to convert the asynchronous Ajax call (which invokes a callback function once the answer has been received from the remote server) into a synchronous call which will not return until the results have been received.
This is a really bad idea. (If you want to know more, ask another question in SO.)
精彩评论