We already know that js already has an established system in its modern form. However, in order to understand why it was formed in this form, we need to look at the history of its formation.
For the sake of clarity, I have prepared a simple application whose source code we will use to trace the evolution of the modular approach.
Namespace pattern (late 90s)
I am sure that this way to organize js applications was the first of its kind and was used almost since the emergence of the language, because it is the most obvious. It, of course, does not pretend to full-fledged modularity, but nevertheless solves the problem of global context.
To use it, it is enough to define one single global object for the whole application, and all variables (as well as functions, classes, etc.) that need their own namespace are written to this global object. In this way, the application developer has the assurance that no external dependencies can affect the operation of his application. The only case is when the name of this global variable coincides with the same name of another plug-in external module, but this is much easier to keep track of.
This approach cannot be called a modular system in full, because it solves only the problem of name collision, but not more than that.
Directly Defined Dependencies (1999-2000)
The first serious swallow in the enterprise. This approach is still used in various applications. Dojo (whose author claims to have invented this method) works on this principle. By the way, Google still uses and supports closure library, because many Google tools in the browser – search, analytics, mail, google docs and other services – are based on it.
If we simplify it a lot, there are 2 functions – provide and require (may be called differently). The first one is responsible for registering the current file as a module, and the second one is responsible for connecting dependencies.
The whole code is divided into many small files that contain separate pieces of code. Each such piece, if it is to be loaded as a module, is labeled with a specific name using the provide function. The name is usually constructed using the namespace pattern and arranged in the file structure of the web server so that the namespace maps to the file address. For example, if you create a module in the namespace application.loader.throbber, then you must ensure that it is located in the file structure at /application/loader/throbber.js. In short – you need to define rules and follow them, for example as it is now common in the php world with PSR autoloading.
Defining dependencies in comments and External dependency definition (2006-2007)
Backend development was developing as fast as Frontend. Back then, there was not as clear a distinction as there is today, and web developers were coming up with new ideas on how to break an application into pieces and put it back together. Around the same time, the concept of assemblers – separate programs designed to prepare the frontend for digestible use in the browser – began to emerge.
ECMAScript 2015 Modules (2015).
In 2015, the standard, which had been in the works since 2010, was released. One of the features described in the standard was a native module system. It is obvious that the key influence on it was the CommonJS standard, as the most popular and convenient at that time.