I wasn't sure how to title this question, but I'm using the revealing module pattern with constructors and noticed some behavior that confused me. The actual purpose of this code is irrelevant to my question I just want to understand this behavior I saw.
Here is the code that I had issues with:
var ajaxModule = (function() {
//private
var data,callBack,type,dataType,url,
//public
constructor;
constructor = function(callBack,data,type,dataType,url) {
//Required
callBack = callBack || null;
data = data || null;
//Defaults
type = type || 'POST';
dataType = dataType || 'json';
url = url || 'DBConnect.php';
};
//methods
function setAjaxQuery() {
log('in setAjaxQuery')
dir(callBack);
dir(data);
dir(type);
dir(dataType);
dir(url);
};
constructor.prototype = {
constructor: ajaxModule,
setAjaxQuery: setAjaxQuery,
};
return constructor;
}());
Now calling it:
var ajax = new ajaxModule('someObject','someData');
ajax.setAjaxQuery();
Now doing this I was seeing undefined for all of those console.dir() commands in the setAjaxQuery function.
I found the problem was that the parameter names were the same as the local variables. I resolved the issue by doing something like this:
constructor = function(zcallBack,zdata,ztype,zdataType,zurl) {
//Required
callBack = zcallBack || null;
data = zdata || null;
//Defaults
type = ztype || 'POST';
dataType = zdataType || 'json';
url = zurl || 'DBConnect.php';
}
My question though is why does this happen? What is javascript doing that causes this problem when the names are the same?
When you add arguments, they reserve the local scope for the names you provide. Since the outer scope has a version of those names that you are trying to access you cannot reuse those names or the collision will result in using the local scope only. Since the second calling function only has access to the outer scope there is no way of retrieving the values set in the inner scope.
Lexical scoping can get weird but let's consider an example:
function x(){
var a = 5; // scoped to x
var b = 10; // scoped to x
function c() {
var a = 10; // scoped to c, this is equivalent to having an argument named a
return a + b; // b is scoped to x
}
return c; // scoped to x
}
My advice for these cases would be to use parameterization to solve the problem:
var ajaxModule = (function() {
//private
var inner = {
data: null,
callBack: null,
type: 'POST',
dataType: 'json',
url: 'DBConnect.php'
},
//public
constructor;
constructor = function(callBack,data,type,dataType,url) {
//Required
inner.callBack = callBack || null;
inner.data = data || null;
//Defaults
inner.type = type || 'POST';
inner.dataType = dataType || 'json';
inner.url = url || 'DBConnect.php';
};
//methods
function setAjaxQuery() {
log('in setAjaxQuery')
dir(inner.callBack);
dir(inner.data);
dir(inner.type);
dir(inner.dataType);
dir(inner.url);
};
constructor.prototype = {
constructor: ajaxModule,
setAjaxQuery: setAjaxQuery,
};
return constructor;
}());
This way you are simultaneously keeping your naming convention and explicitly declaring your scope via (inner).