There are several patterns for structuring modules in JavaScript. The most common ones I see talked about is the original JavaScript Module Pattern, AMD Modules, CommonJS Modules, and the emerging Harmony Modules.
The large JavaScript application that I work on at work - an in-browser IDE - does not presently use any of these well-known patterns but instead uses what I’m going to call the Flat Module Pattern.
Here’s what the Flat Module Pattern looks like, for an excerpt of a real module called save.js
:
/*public*/ function setupSave() {
if (isMainOrStandaloneWindow()) {
setupSaveToken();
}
}
// -----------------------------------------------------------------------------
// Autosave
var DELAY_AFTER_TYPING_STOPS_TO_AUTOSAVE = 2000;
/*public*/ function autosave(fileInfo) {
...
}
// -----------------------------------------------------------------------------
// Save Token
function setupSaveToken() {
...
}
/*public*/ function isUnsafeToSave() {
...
}
// -----------------------------------------------------------------------------
Notice that:
/*public*/
. A module should not attempt to reference a non-public method in a different module.Consequences of this pattern:
<script>
-included in any order, since there are no side-effects of including a module.All such modules on a particular page are directly included by the HTML page:
{% compress js %}
<!-- Library -->
<script src="{% static 'tsk_platform/student_survey.js' %}"></script>
<script src="{% static 'tsk_platform/util/dialog.js' %}"></script>
<script src="{% static 'tsk_platform/util/markdown.js' %}"></script>
<script src="{% static 'tsk_platform/util/slider.js' %}"></script>
<script src="{% static 'tsk_platform/util/url.js' %}"></script>
<script src="{% static 'tsk_platform/util/websocket.js' %}"></script>
<script src="{% static 'tsk_platform/vendor/jquery.min.js' %}"></script>
<script src="{% static 'tsk_platform/vendor/mdl/material.min.js' %}"></script>
<!-- Page-Specific -->
<script src="{% static 'ide/code/__init__.js' %}"></script>
<script src="{% static 'ide/code/panels/__init__.js' %}"></script>
<script src="{% static 'ide/code/panels/assignment/__init__.js' %}"></script>
<script src="{% static 'ide/code/panels/assignment/code_comprehension.js' %}"></script>
<script src="{% static 'ide/code/panels/assignment/code_writing.js' %}"></script>
<script src="{% static 'ide/code/panels/code_instructions.js' %}"></script>
<script src="{% static 'ide/code/panels/project.js' %}"></script>
<script src="{% static 'ide/code/panels/slides.js' %}"></script>
<script src="{% static 'ide/code/run/__init__.js' %}"></script>
<script src="{% static 'ide/code/run/engine/java.js' %}"></script>
<script src="{% static 'ide/code/run/engine/python.js' %}"></script>
<script src="{% static 'ide/code/run/run_window.js' %}"></script>
<script src="{% static 'ide/code/tabs/__init__.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/blocks.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/font.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/image.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/scene.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/sound.js' %}"></script>
<script src="{% static 'ide/code/tabs/editor/text.js' %}"></script>
<script src="{% static 'ide/code/tabs/save.js' %}"></script>
<script>
setupCodePage();
</script>
{% endcompress %}
In considering how modules may reference members of each other:
The Flat Module Pattern is incredibly simple to follow, with almost no boilerplate. It is also easy to concatenate and minify.
My team has been using this pattern successfully over the last 4 years or so with a codebase that is now approximately 55,000 lines of JavaScript. I’m pretty happy with it.
The one major drawback of the above Flat Module Pattern is that public functions are advisory-only since they are just marked with /*public*/
comments. It is still possible (and easy) to accidentally improperly reference a non-public function from outside the module it is declared in.
So here’s an enhanced variation I call the Encapsulated Flat Module Pattern that doesn’t have that problem:
(function() {
function public(func) { window[func.name] = func; }
public(function setupSave() {
if (isMainOrStandaloneWindow()) {
setupSaveToken();
}
});
// -----------------------------------------------------------------------------
// Autosave
var DELAY_AFTER_TYPING_STOPS_TO_AUTOSAVE = 2000;
public(function autosave(fileInfo) {
...
});
// -----------------------------------------------------------------------------
// Save Token
function setupSaveToken() {
...
}
public(function isUnsafeToSave() {
...
});
// -----------------------------------------------------------------------------
})();
Notice that each /*public*/
advisory comment has been replaced with a call to a special public(...)
function that actually exports the provided function. Any function that is not marked as public is truly private and not available for reference by other modules.
Benefits of this pattern:
I’ve found these JavaScript module patterns to be very useful in structuring JavaScript application code I’ve written over the last few years.
I think these patterns would work well in any large JavaScript application that avoids class inheritance1 and has reasonable non-colliding public function names.
I do not think these patterns would be a good idea for JavaScript libraries that need to avoid polluting the global namespace. For such libraries, falling back on the Universal Module Definition pattern seems more prudent.
Supporting class inheritance requires serializing the order of top-level definitions since the definition of a subclass depends of the definition of its superclass, and therefore subclasses must be declared after its related superclass. The module patterns in this article do not serialize top-level definition order and therefore cannot reliably support subclasses being declared in a different module than its superclass.↩