I understand that using closures (IIFE) is the best practice as it prevents polluting the global namespace. However, when I added the closures to my files, it prevented my 2nd file (controllers.js) from reading the first file (models.js). To give you an idea, here's what they look like:
models.js
;(function() {
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})();
controllers.js
;(function() {
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
Now that I've added closures on them, I'm getting an error that searchResult
is undefined in controllers.js -- because it can't see that it exists in the models.js. How do I get it to understand that it exists in the other file?
P.S. Yes, models.js is added in the HTML file before the controllers.js file.
For them to interact, they have to have some common symbol. You have a couple of choices:
Do it yourself (using a single global variable)
Use some kind of library that does it for you (using [ideally] just a single global symbol)
Do it yourself a different way that requires no global common symbol at all
The DIY version is, typically, that you have a single global, for your entire app, which your various modules add properties to.
So for instance:
models.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
MyApp.searchResult = searchResult;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})(this);
That works because in loose mode, this
at global scope is the global object (window
on browsers). We pass it into the IIFE as the argument globals
, and then either use or create a property on it called MyApp
, and add searchResult
to it as a property.
controllers.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
function storeSearchResults(jsonObj) {
var instance = new MyApp.searchResult(jsonObj.data[i]);
/* Do more */
}
})(this);
We do the same thing, except that controllers.js
is expecting that models.js
has already been run. Although we still do the var MyApp = globals.MyApp = globals.MyApp || {};
bit, the new MyApp.searchResult
would of course fail if models.js
hadn't been run.
There are probably a dozen syntactic variations on this theme, this is just one of them.
Your other option is to use a library like RequireJS (the one global symbol there is require
, and it's a function) or any other asynchronous module definition lib.
Another DIY option gets rid of globals entirely, you don't even need a single global.
To do that, your individual files don't have the IIFE (although they can use ones for things they don't want to share with other files):
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
controllers.js:
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
Then you use a minifier to combine your scripts and wrap them in one big IIFE. You might have pre.js
:
(function() {
and post.js
:
})();
Then the minifier creates app.js
by combining pre.js + models.js + controllers.js + post.js
. The end result (un-minified and formatted here for readability) is:
(function() {
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
I called this DIY, but I wouldn't be surprised if there were tools to help with it.