Historical background
In the early days of web development, for the sake of teamwork and code maintenance, some developers would write JavaScript code in separate files and load them with multiple script tags.
<script src="./common.js"></script>
<script src="./tools.js"></script>
<script src="./others.js"></script>
Copy the code
There is nothing wrong with writing this, but there is a very big problem that all JS variables will be in the same global scope, and there is an extra concern about the problems caused by scoped variable promotion.
<script>
// common.js
var name = 'Wood Dun -- The Flowers and Trees come'
setTimeout(() = > console.log(name), 2000)
</script>
<script>
// others.js
var name = 'The Mudescape - The tree kingdom descends'
</script>
Copy the code
The name variable declared in common.js will be overwritten by the name variable in others.js. After 2 seconds, the name variable declared in the others.js file will be overwritten by the name variable in the others.js file. The name variable in the common.js file will change to “mudescape — tree bound descends”.
Another problem is that if the imported file has dependencies between code blocks, you need to pay extra attention to the order in which the script is loaded. If the file dependencies are changed, you need to manually change the order of the loading tags in the HTML.
To solve this problem, you need to “modularize” these script files
- Each module must have its own variable scope, and internal variables between the two modules will not conflict
- Different modules directly retain the way of importing and exporting each other, and modules can communicate with each other. The execution and loading of modules follow certain norms to ensure the dependence between each other.
CommonJS specification
Node.js is a V8-powered, event-driven I/O server-side JAVASCRIPT runtime environment that implements a modular specification called CommonJS when it first came out in 2009.
In the CommonJS specification, each JS file is a module, and each module can be imported and exported using the require function and module.exports object. The sample
/ / sample
const a = require('./a') // Get the result of exporting file A
module.exports = a // Export the value of a inside the current module
Copy the code
/ / sample 2
// index.js
require('./a.js)
const num = require('./b.js)
console.log(num)
// a.js
var num = require('./b.js) setTimeout(() => console.log(num), 1000) // b.js var num = new Date().getTime() module.exports = numCopy the code
- index.jsFile by
require
Function, loaded separatelya.js
andb.js
Both modules output at the same timeb.js
Results in the module. - a.jsThe document is approved
require
The function is loadedb.jsModule, after 1 second, also outputs the result of loading module B. - b.jsThe file defines a variable num and passes
module.exports
Export num.
The relationship between the three modules
When you run the node index.js command from Example 2 and look at the output, you can see that both lines of output are the same, no matter how many times you execute them.
Next, change the code in module B again as follows:
// b.js
let m = new Date().getTime()
setTimeout(() = > {
m = new Date().getTime()
console.log(m, 'THIS is module B, one second later:', m)
}, 1000)
module.exports = m
Copy the code
The above code reassigns the variable m after one second and prints it, leaving the rest of the files untouched. Then execute the node index.js command again to see:
Js, b.js, and a.js. The first line is the same as the third line, but the second line is different.
The above examples are all examples of using data of non-reference types (basic data types). Here’s how to adjust the code of B. js:
// b.js
let m = {
a: 123
}
setTimeout(() = > {
m.a = 456
console.log(m, 'THIS is module B, one second later:', m)
}, 1800)
module.exports = m
// a.js
const m = require('./b.js')
setTimeout(() = > console.log(m, 'I'm module A.js'), 1500)
// index.js
const a = require('./a.js')
const b = require('./b.js')
console.log(b, 'I'm module index.js')
Copy the code
In order to improve the identification, a description is added in the print statement of each module. In addition, the variable M in the MODULE of B. js is assigned to a reference type. As we all know, the value of reference type data in JS is an Object stored in the Heap. When I change the value of attribute A in the m object, the values of other places where variable M is used will also change. OK, knowing this then execute the node index.js command and look at the output:
As you can see, the values of the module index.js and the module a.js loaded in b.js are unchanged.
Through the above small examples, it can be found that the two problems mentioned at the beginning of the article are perfectly solved. Here is a summary:
- Modules directly internal even if they have the same variable name, they run without conflict. Indicates that it has the ability to handle the scope of module variables.
b.js
Module bymodule.exports
An internal variable is exported, and it is ina.js
andindex.js
Two modules can be loaded.It can import and export modules and handle basic dependencies.- All loaded in different modules
b.js
Module, and the result is the same.Indicates that it guarantees the module singleton.That is, once a value is printed, changes within the module do not affect that value. - As you can see from the sample code execution, CommonJS is aSynchronously loading modulesModularity specification for each module
require
A submodule stops parsing of the current module until the submodule reads parsing and loads it.
As you can see from the four points above, such CommonJS modules can only be run in node.js environments. Running such modules directly in other environments will result in an error. This is because only the node in the process of parsing JS provides a method of the require, so that when the parser code execution, found a module to invoke the require function, will find the corresponding module through parameters of physical path, through the system call from the hard disk read from the file content, parse it eventually getting the result of export and return. Other runtime environments may not necessarily provide such a require method at parsing time.
AMD Modular Specification
In addition to CommonJS code running on the server via Node.js, the only other environment that allows JAVASCRIPT to run is the browser. However, browsers do not provide the require method as in Node.js. However, inspired by CommonJS modular specification, the WEB side gradually developed AMD, SystemJS specification and other suitable for browser side to run JS modular development specification.
AMD full name Asyncchronous Module definition, meaning ** asynchronous module definition, different from the CommonJS specification of synchronous loading, AMD all modules are asynchronous loading by default, if the web side also use synchronous loading, Then the page may pause the response while parsing the script file.
The definition of AMD modules is a bit different from CommonJS, so let’s tweak the above example slightly:
// index.js
require(['a'.'b'].function(a, b) {
console.log(b, 'I'm module index')})// a.js
define(function(require) {
var m = require('b')
setTimeout(() = > console.log(m, 'I'm module A'), 1500)})// b.js
define(function(require) {
var m = new Date().getTime()
return m
})
Copy the code
AMD modules also support file level modules, module ID default file name, and can load multiple modules in parallel, but not on demand, must load the required dependencies in advance, in this module file, you need to use define function to define a module, in the callback function to accept the definition of component content. This callback takes a require method that allows it to load other modules inside the component, passing in module ids to load AMD modules in the corresponding file. The return value of this callback is the result of the module export.
The require function takes two arguments. The first argument specifies the dependency table of the entry module, and the second argument, as a callback argument, passes in the exported values of the previous dependencies. In the above example, in the index.js function, only the value passed in by B is printed.
Unlike the CommonJS specification, node.js allows you to view the output of a module directly through Node index.js. In AMD, since it runs on the WEB, it requires an HTML file and loads the entry module in it. So add an index.html file as a launch point in your browser.
If you want to use the AMD specification, you need to add an AMD-compliant loader script to the page. There are many amD-compliant libraries, such as RequireJS, curl, Dojo, Nodules, etc. Here, require.js is used
<! DOCTYPEhtml>
<html lang="en">
<! -... Omit some code -->
<! AMD modules must be loaded with require.js and other modules that are compatible with the AMD module library.
<script src="./require.js"></script>
<! -- Load entry module only -->
<script src="./index.js"></script>
<! -... Omit some code -->
</html>
Copy the code
The relationship between modules in the above example is as follows:
Run the above code in a browser and then adjust the code so that the code is not duplicated here, as is the logic of the sample content used in the CommonJS specification above. Here is the result:
The basic data type case
Reference data type cases
As you can see from the above example, AMD and CommonJS have solved the problem of variable scope dependency, but AMD’s asynchronous default, which defines the module content in the callback function, is a bit cumbersome to use. In addition, AMD modules cannot run on the Node side, because the internal define and require functions must be used in conjunction with loading libraries such as require.js in the browser.
CMD specification
CMD specification is ali yubo put forward, implement JS library for sea-.js. It is very similar to Requirejs in that a JS file is a module, but CMD is loaded on demand rather than having to load all dependencies at the start of the module. I won’t show the code examples here (mainly because I’m lazy).
UMD specification
Sometimes we write modules that need to run in the browser environment and Node.js at the same time, so we need to write a AMD module and a CommonJS module respectively to run in their respective environment. If there are changes in module content every time, we need to change these two modules, which is very troublesome.
Based on this problem, UMD(Universal Module Definition) emerged as a modular solution to isomorphic, enabling us to define Module content in only one place and compatible with both AMD and CommonJS syntax.
Writing a UMD module is very simple. We just need to determine the characteristic values of these modularity specifications, determine the current modularity specification environment, and export the module content using the detected modularity specification syntax.
(function(self, factory) {
if (typeof module= = ='object' && typeof module.exports === 'object') {
// The current environment is the CommonJS specification environment
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// The current environment is the AMD specification environment
define(factory)
} else {
// No environment, just hang on the global object
self.umdModule = factory();
}
}(this.function() {
return function() {
return Math.random(); }}));Copy the code
Module. exports (CommonJS, CommonJS, CommonJS, CommonJS, CommonJS, CommonJS, CommonJS); If the current environment has define and define. Amd is true, you can use AMD define to define a module. If neither of the above matches, you can mount the module content directly to the global object, which can also be loaded into the module export result.
ESModule specification
After EcmaScript 2015 (ES6), JS implements modularization function on the language standard level. When using ESModule, you can import and export modules by using import and exprot keywords.
Let’s continue with the above example and adjust the code as follows:
// index.js
import './a.js'
import m from './b.js';
console.log(m, 'I'm module index')
// a.js
import m from './b.js'
setTimeout(() = > console.log(m, 'I'm module A'))
// b.js
const m = new Dtae().getTime()
export default m
Copy the code
The biggest difference between ESModule and CommonJS and AMD is that ESModule is implemented by the JS interpreter, while the latter two are implemented at runtime in the host environment. The ESModule import actually adds a new statement at the syntactic level, while AMD and CommonJS load modules by actually calling the require function.
Index. js:2 Uncaught SyntaxError: Unexpected Identifier For CommonJS and AMD, just add a require function to ensure that they do not report syntax errors
The ESModule specification supports importing and exporting code in these ways, depending on how it is exported:
Example 1:
// index.js
import {a, b} from './other.js'
// other.js
export const a = () = > {}
export const b = 12345678
Copy the code
Example 2:
import tools from './utils.js // util.js const add = (a, b) => a + b const filterData = (arr, id) => arr.filter((val) => val ! == id) export default {add, filterData} // let obj = {add, filterData} export default objCopy the code
Example 3:
import * as colors from './theme.js'
Copy the code
For export and export default, export default essentially prints a variable or method called default. There’s one thing to note: Import {a, b} from ‘./other.js’ The parentheses here do not mean that the result obtained is an object, not the structural syntax of objects in ES6
Each JS runtime environment is an interpreter, otherwise it will not understand THE JS syntax, the role of the interpreter is to use ECMAScript specification to recognize THE JS syntax, that is, to process and execute the content of the language itself, according to the language logic to correctly execute
On top of the interpreter, each runtime environment encapsulates some environment-specific apis on top of the interpreter, such as global and Process objects in Node.js, window and Document objects in the browser, and so on. The apis of these runtime environments are influenced by their respective specifications, such as the W3C specification on the browser side, which specifies the API content on window objects and Document objects, So that we can get apis like Document.getelementById to work on all browsers.
conclusion
Here is a summary of the knowledge points mentioned above
CommonJS specification
- Modules are run time loaded, CommonJS modules are objects; That is, the entire module is loaded on input, an object is generated, and methods are read from that object. This loading is called “runtime loading.”
- A module outputs a copy of a value, and once it outputs a value, changes within the module do not affect that value.
- Modules load synchronously. Each time a module loads a submodule, parsing of the current module is stopped until the submodule reads and loads
- Run on the server (because of synchronization, it will cause blocking, server modules are local, there is no network request, etc.)
AMD specification
- Dependency prefixes, that is, other modules on which the current module depends, must be resolved before the specific callback function is actually executed
- A module outputs a copy of a value, and once it outputs a value, changes within the module do not affect that value.
- Modules load asynchronously, allowing callback functions to be specified
- Run on the browser side
CMD specification
Similar to the AMD specification, the main differences are as follows
- For dependent modules, AMD executes early and CMD executes late. Since 2.0, however, RequireJS has been changed to defer execution (depending on how it is written, it can be handled differently).
- CMD advocates dependency nearby, AMD advocates dependency front.
- AMD API default is a when multiple use, CMD API strictly differentiated, advocating a single responsibility. For example, in AMD, require is divided into global require and local require, both called require. In CMD, there is no global require, but according to the completeness of the module system, seajs.use is provided to realize the loading and starting of the module system. In CMD, every API is simple and pure.
ESModule
-
It can run on the server and in the browser
-
Modules are loaded at compile time. ES6 modules are not objects, but output code explicitly specified through the export command. Import takes the form of static commands. That is, when you import, you can specify that an output value is loaded instead of the entire module, which is called “compile-time loading.”
-
The module outputs references to values. When the JS engine statically analyzes the script, it generates a read-only reference when it encounters the module load command import. 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.
-
Module variable is read-only. ES6 input module variable is just a “symbolic link”, so this variable is read-only and reassigning it will report an error.
Reference article:
- Blog.csdn.net/zhixjs/arti…
- www.cnblogs.com/cag2050/p/7…
- www.zhihu.com/question/20…
- Blog.csdn.net/weixin_4245…
- Javascript.ruanyifeng.com/nodejs/modu…