
Creating JS Modules
Hello!
In this article I want to tell you about my approach to writing modules in JavaScript. Professionals are unlikely to find something new for themselves, but for beginners, I think it will be useful to familiarize themselves with the proposed approach and the arguments in its favor.
When I create a new js file in my favorite IDE, the following code is immediately inserted into the editor window:
Consider what exactly happens on these few lines.
First of all, a closed namespace is created by putting the code in a self-invoking anonymous function. At the same time, two parameters
This line includes strict mode:
I use strict mode for many reasons. Not only is its presence required for correct code validation using JSLint , but its use also blocks many unsafe constructs.
It would seem a completely innocent piece:
What pitfalls can be here? It's simple: somewhere earlier in the code, a variable
Such a check will force you to not only determine all the variables before use, but also force you to check the loop arguments once again.
In addition, in strict mode, accessing an undeclared variable leads to a performance error, while in normal mode, the JavaScript interpreter tries to create this variable and assign it some value.
For instance:
If the variable has
What if the target browser does not support strict mode? Nothing. Code written for strict mode will work seamlessly without problems, and the JavaScript interpreter will simply ignore the line
After the instruction
The variables
In addition, when using tools such as YUICompressor or Google Closure Compiler, the names of these variables will be reduced to one or two letter:
All variables declared here will be private and visible only within the module. If we need to create a generator function for unique id elements, this will be very simple:
But we can not send this function for export, but use it as a service function inside the module:
Perhaps all of your modules will be designed as a single library, called, for example, JSui, and all functions should be called like this:
It would be possible to assemble everything into one file, expanding a single object and exporting it to the global namespace, but debugging and editing several small modules is always easier than one large one. Therefore, we will use only one global variable and, if necessary, expand it with the necessary properties. I do it like this: divs.js
file :
Labels.js file :
Some will say that assignment is
If a global object is
PS I did not touch on the topic of resolving module dependencies, because this is a separate topic that deserves an entire article.
In this article I want to tell you about my approach to writing modules in JavaScript. Professionals are unlikely to find something new for themselves, but for beginners, I think it will be useful to familiarize themselves with the proposed approach and the arguments in its favor.
The module "from scratch"
When I create a new js file in my favorite IDE, the following code is immediately inserted into the editor window:
(function (G, U){
"use strict";
var $ = G.jQuery,
bool = "boolean",
string = "string",
number = "number",
object = "object";
}(this, undefined));
Consider what exactly happens on these few lines.
1. Creating a private namespace.
First of all, a closed namespace is created by putting the code in a self-invoking anonymous function. At the same time, two parameters
G
and are transferred to the anonymous function U
, to which the this
and values are assigned undefined
. Question: what in this case will be equal this
? It depends on which platform you are working with, but in any case it will be a global namespace, such as an object in browsers window
.2. Using strict mode.
This line includes strict mode:
"use strict";
I use strict mode for many reasons. Not only is its presence required for correct code validation using JSLint , but its use also blocks many unsafe constructs.
It would seem a completely innocent piece:
for (i = 0; i < items.length; i += 1){
//Что-то делаем
}
What pitfalls can be here? It's simple: somewhere earlier in the code, a variable
i
has already been declared, and this loop is nested:for (i = 0; i < myArr.length; i += 1){
//Около 50 строк кода, обычно хватает, чтобы скрыть начало тела цикла
for (i = 0; i < myArr.length; i += 1){
//Что-то делаем
}
}
Such a check will force you to not only determine all the variables before use, but also force you to check the loop arguments once again.
In addition, in strict mode, accessing an undeclared variable leads to a performance error, while in normal mode, the JavaScript interpreter tries to create this variable and assign it some value.
For instance:
alert("Ошибка доступа: "+ error);
If the variable has
error
not been previously declared, in normal mode the user will be able to admire the message "Ошибка доступа: undefined"
. In strict mode, this variable must be at least defined. What if the target browser does not support strict mode? Nothing. Code written for strict mode will work seamlessly without problems, and the JavaScript interpreter will simply ignore the line
"use strict";
.Private module variables
After the instruction
"use strict";
that includes strict mode, several variables are described. As you can see, I follow the “one var statement” pattern , also recommended for use. You must admit that the construction described above does not look as terrible as the one below:var $ = G.jQuery;
var bool = "boolean";
var string = "string";
var number = "number";
var object = "object";
The variables
bool
, string
, number
and object
below I describe for greater convenience:if (params.hasOwnProperty("title") && typeof params.title === string){ //где-то в коде
result.title = params.title;
}
In addition, when using tools such as YUICompressor or Google Closure Compiler, the names of these variables will be reduced to one or two letter:
if (p.hasOwnProperty("title") && typeof p.title === s){
r.title = p.title;
}
All variables declared here will be private and visible only within the module. If we need to create a generator function for unique id elements, this will be very simple:
(function (G, U){
"use strict";
var id = 0,
PREFIX = "my-library-id-prefix-";
function getNewId(){
id += 1;
return PREFIX + id.toString();
}
G.createId = getNewId; //Экспорт функции getNewId() в глобальное пространство имён под именем createId
}(this, undefined));
But we can not send this function for export, but use it as a service function inside the module:
function Div(params){ //Функция-конструктор для новых блоков
var id = getId(); //id блоков всегда будут уникальны, испортить генератор внешним кодом невозможно this.show = function(){ $("
", { "id": id }); } }
Inclusion of the module in the library
Perhaps all of your modules will be designed as a single library, called, for example, JSui, and all functions should be called like this:
var newDiv = new JSui.Div({
width: 200,
height: 150
});
It would be possible to assemble everything into one file, expanding a single object and exporting it to the global namespace, but debugging and editing several small modules is always easier than one large one. Therefore, we will use only one global variable and, if necessary, expand it with the necessary properties. I do it like this: divs.js
file :
(function(G, U){
"use strict";
var UI= G.JSui || {};
//Код модуля
function Div(){
...
}
UI.Div = Div;
G.JSui = UI;
}(this, undefined));
Labels.js file :
(function(G, U){
"use strict";
var UI= G.JSui || {};
//Код модуля
function Label(){
...
}
UI.Label = Label;
G.JSui = UI;
}(this, undefined));
Some will say that assignment is
G.JSui = UI;
clearly superfluous. But do not make hasty conclusions! What if the specified module is connected first and the global variable has JSui
not yet been defined? The local variable UI
will be assigned an empty object {}
. Further in the module code, we expand it, but export to the global namespace does not occur until the line is called: G.JSui = UI;
If a global object is
JSui
already defined, the local variable UI
gets a reference to it and extends it with new properties and methods. In this case, indeed, the above assignment will be redundant, however, we should not forget the simple fact that in this case the object will be passed by reference and performance will not suffer. PS I did not touch on the topic of resolving module dependencies, because this is a separate topic that deserves an entire article.