Before we start, please read this section carefully to understand the CommonJS, AMD, ES6 modules.
Focus of the module
The back-end JavaScript sits on both sides of HTTP (the back-end JS stands for Node) and plays different roles.
JavaScript in a browser needs to be distributed from the same server (Web server) side to multiple client side execution, whereas server-side JavaScript is the same code that needs to be executed multiple times. The bottleneck of the former lies in bandwidth, while the bottleneck of the latter lies in resources such as CPU and memory. The former requires code to be loaded over the network, and the latter from disk, which are not in the same order of magnitude of speed.
** If you look at Node’s module import process (CommonJS require), it’s almost all synchronous. ** While somewhat counter to Node’s emphasis on asynchronous behavior, it makes sense. Front-end JavaScript and UI rendering share the same thread, and if front-end modules are introduced in a synchronous manner, it can cause major problems for the user experience. UI initialization takes a lot of time waiting for scripts to load.
For network reasons, the CommonJS specification for back-end JavaScript is not entirely appropriate for front-end scenarios. After some wrangling, the AMD specification finally won out in the front-end application scenario. Its full name is Asynchronous Module Definition. See github.com/amdjs/amdjs… . In addition, there is the CMD specification defined by Yubo.
So:
For the back end, synchronous loading is no problem because:
- Modules are local, and the wait time is the hard disk read time.
- Reliability is more important than startup time.
For the front end:
- Modules are all on the server and need to be requested over the network, too slow.
- Syncing XHR will clog the browser, the user experience will be poor with fee-dead, and the first screen time is important.
The CommonJS specification applies primarily to the implementation of Node modules, which are not implemented exactly but are generally the same.
The AMD specification should be used for asynchronous loading of front-end JavaScript.
After the release of ES2015, the new module introduction method ES6 Module appears. **ES6 loading modules does not specify synchronous or asynchronous, and how ES6 defined modules are loaded may depend on the environment in which the code is running. ** It can become a common modular solution for browsers and servers.
AMD specification
The AMD specification is an extension of the CommonJS module specification. It loads modules asynchronously without affecting subsequent statements. All statements that depend on this module are defined in a callback function that will not run until the load is complete.
Its modules are defined as follows:
define(id? , dependencies? , factory);Copy the code
Its module ID and dependencies are optional, similar to the Node module in that the contents of the Factory are the contents of the actual code. The following code defines a simple module:
define(function(){
var exports = {};
exports.sayHello = function(){
console.log('Hello from module: ' + module.id);
};
return exports;
});
Copy the code
Unlike CommonJS, AMD modules require define to explicitly define a module, while Node implementations are implicitly wrapped. Their purpose is to be scoped out and only introduced when needed, avoiding the old approach of using global variables or global namespaces. Side variables are contaminated and accidentally modified. Another difference is that the content needs to be exported as a return.
Differences between ES6 modules and CommonJS modules
ES6 modules differ from CommonJS modules in three major ways:
- The CommonJS module prints a copy of the value, the ES6 module prints a reference to the value.
- The CommonJS module is run time loaded, and the ES6 module is compile time output interface
- At the top of the CommonJS module
this
Pointing to the top level of the current module, ES6 modulethis
Point to theundefined
Note ⚠️, the top-level (default) export default of the ES6 module is a copy of the value, which is inconsistent with the behavior of the secondary export.
This is because the content after export default is treated as an expression, the value of which is assigned to an implicit variable, and the final output is an implicit variable. (Except for function and class, which v8 does not treat as expressions because they return undefined as expressions)
If you want to export references to values by default, you can use export {XXX as default} instead of export default XXX.
Reference: jakearchibald.com/2021/export…
The second difference is because CommonJS loads an object (that is, the module.exports property) that is generated only after the script has run. An ES6 module is not an object, and its external interface is a static definition that is generated during the code static parsing phase.
The first difference is highlighted below.
CommonJS
The CommonJS module outputs a copy of the value, meaning that once a value is output, changes within the module do not affect that value.
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var mod = require('./lib');
console.log(mod.counter); / / 3
mod.incCounter();
console.log(mod.counter); / / 3
Copy the code
As the code above shows, once the lib.js module is loaded, its internal changes do not affect the output mod.counter. This is because mod.counter is a primitive value and will be cached. You have to write it as a function to get the internal value.
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
get counter() {
return counter
},
incCounter: incCounter,
};
Copy the code
In the code above, the output counter property is actually a valuer function. Now, by executing main.js, you can correctly read the changes to the internal variable counter.
$ node main.js
3
4
Copy the code
ES6 module
ES6 modules operate differently from CommonJS.
The JS engine encountered a module load command while statically analyzing the scriptimport
, a read-only reference is generated. When the script is actually executed, it will be evaluated in the loaded module based on the read-only reference.
Therefore, ES6 modules are referenced dynamically and do not cache values. Variables in modules are bound to the module in which they are located.
Let’s take the example above.
// lib.js
export let counter = 3;
export function incCounter() {
counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); / / 3
incCounter();
console.log(counter); / / 4
Copy the code
Since the module variable entered by ES6 is just a “symbolic link”, it is read-only and reassigning it will report an error.
// lib.js
export let obj = {};
// main.js
import { obj } from './lib';
obj.prop = 123; // OK
obj = {}; // TypeError
Copy the code
In the above code, main.js inputs the variable obj from lib.js. You can add attributes to obj, but reassigning to obj will return an error. Because the address to which the variable obj points is read-only and cannot be reassigned, it is as if main.js created a const variable named obj.
** Even if an ES6 module is compiled as a CommonJS module using Babel or other tools, running the es6 module through NodeJS will still be a copy of the value, which does not reflect the characteristics of the ES6 module itself. ** This may be due to the difference between the browser’s JS engine and node JS engine.
Finally, let’s look at this example.
Export outputs the same value through the interface. Different scripts load the interface and get the same instance.
// mod.js
function C() {
this.sum = 0;
this.add = function () {
this.sum += 1;
};
this.show = function () {
console.log(this.sum);
};
}
export let c = new C();
Copy the code
The above script mod.js outputs an instance of C. Different scripts load the module and get the same instance.
// x.js
import {c} from './mod';
c.add();
// y.js
import {c} from './mod';
c.show();
// main.js
import './x';
import './y';
Copy the code
Now execute main.js and the output is 1.
$ babel-node main.js
1
Copy the code
This proves that x.js and y.js load the same instance of C.
Based on the ES6 module, we can easily implement publish-subscribe event pools.
reference
-
Introduction to ES2015 Standard (3rd edition) — Ruan Yifeng
-
NodeJS – Park Ling
-
Stackoverflow.com/questions/3…