For the module pattern, I'm doing something like:
(function(namespace) {
// tons of code
// blabla
})(window.myGlobalNamespace);
How do I split the code? I can think of a few ways, like use a hierachy of namespaces, or expand the object outside by window.myGlobalNamespace.additionalFunc = function () {//blabla}
. What are the other ways? What are the pros and cons? Which one is considered better practice?
Both of the two answers suggest RequireJS. Can you please explain how RequireJS can solve these problems:
first.js:
(function(context) {
var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);
second.js:
(function(context) {
this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);
main.js:
window.myGlobalNamespace.subNamspace.childFunction(); // doesn't work
And people can do
window.myGlobalNamespace.subNamspace.childFunction = function() {alert("a new function");}
to change my code's behaviour!
Here, there are two problems:
We can't have a field that's accessible by child but not to outside public (i.e. protected). Is there any way to achieve that?
If not, meaning if we wanteparentPrivate to be accessible, we need to make it public. Then the user will be able to modify it!
What's more, all the public functions can be altered and replaced. I don't want that to happen.
I don't see how RequireJS solves these problems. Can someone shed some light?
There are only 2 ways to get JavaScript into HTML:
<script> some JavaScript </script>
<script src='main.js'></script>
I know this is obvious but we need that common ground for what comes next. ;)
JavaScript does not have the ability to "import" other JavaScript files into it's self. All the "importing" is done in the HTML. You can do this several ways:
Dynamically link them in through some JavaScript
var script = document.createElement("script");
script.src = "all.js";
document.documentElement.firstChild.appendChild(script);
Library like RequireJS. RequireJS uses Asynchronous Module Definition (AMD) API. It is the JavaScript mechanism for defining modules such that the module and its dependencies can be asynchronously loaded.
It is import to consider reasons for separating JavaScript into separate files.
Separate JavaScript files DO NOT make things Private, Closures make things Private.
Now, consider at the end of the day when everything is ready for production the best thing you could do is Optimize your JavaScript by combining it all into one file so that the user only has one file to download.
When dealing with Private variables in JavaScript, you will at some point want to access them.
Let me illustrate with some code.
module-test.html and main.js (merged first.js, second.js, and main.js for easier testing)
var MODULE = (function () {
//Private variables
var privateParent,
app;
privateParent = 'parentPrivate';
return app = {
//Privileged method
getPrivateParent: function() {
return privateParent;
}
};
}());
MODULE.sub = (function (parentApp) {
//Private variables
var childMessage,
Constr;
childMessage = ' - trying to access parent private field: ' + parentApp.getPrivateParent(); //prints parentPrivate
Constr = function () {
this.childF = this.childFunction();
};
//Constructor
Constr.prototype = {
constructor: MODULE.sub,
version: "1.0",
childFunction: function () {
$("#testing-div").append(childMessage + "</br>");
}
};
return Constr;
}(MODULE));
//We could just as easily print to the console, but the 'append' allows us to display the results on the page.
$("#testing-div").append("This first part shows what <b>does not work</b>; everything is 'undefined'. " + "</br>");
$("#testing-div").append("You are unable to access the var or func directly. " + "</br>");
$("#testing-div").append("MODULE.privateParent = " + MODULE.privateParent + "</br>");
$("#testing-div").append("MODULE.app = " + MODULE.app + "</br>");
$("#testing-div").append("MODULE.sub.childMessage = " + MODULE.sub.childMessage + "</br>");
$("#testing-div").append("MODULE.sub.Constr = " + MODULE.sub.Constr + "</br>");
$("#testing-div").append("MODULE.sub.childFunction = " + MODULE.sub.childFunction + "</br>");
$("#testing-div").append("END lesson. You must access childFunction() through the <b>new</b> operator." + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Let's see if making an instance of the Object works" + "</br>");
var test = new MODULE.sub();
test.childFunction(); //run the method
$("#testing-div").append("Looks like it did!!!!" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Now let's try to change the childFunction() ?" + "</br>");
test.childFunction = function() {$("#testing-div").append(" - This is a new function." + "</br>");}
test.childFunction(); // altered version
$("#testing-div").append("Looks like it was changed. :(" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Does it stay changed?" + "</br>");
var test2 = new MODULE.sub();
test2.childFunction(); // doesn't work
$("#testing-div").append("NO, it was only Overriden in the 'test' Object. It did not effect all the other new objects. :)" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Module Test</title>
<!-- <script data-main="scripts/main" src="scripts/require.js"></script> -->
</head>
<body>
<h1>This is a test for separate Modules and Private variables.</h1>
<div id="testing-div">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="main.js"></script>
</body>
</html>
If you want to use RequireJS to accomplish the above, you can. RequireJS uses the Module Pattern which is what you and I are already using. If you want to separate out the files then there are two ways to do this.
NOTE: I haven't had a chance to compare these two options yet but wanted to include them for completeness.
You may find the following references helpful: