Date: November 2, 2021

Javascript modular programming (A) : module writing — Ruan Yifeng

Javascript modular programming has become an urgent need. Ideally, developers only need to implement the core business logic, and the rest can be loaded with modules someone else has already written.

However, Javascript is not a modular programming language, and it does not support “classes,” let alone “modules.” (Version 6 of the ECMAScript standard, currently under development, will officially support “classes” and “modules,” but it will be a long time before it becomes practical.)

– 2012/10/26

A brief history of JS modularity

CommonJS ➡️ NodeJs ➡️ RequireJs(AMD) ➡️ SeaJs(CMD) ➡️ ES6 module

CommonJS, AMD, CMD, ES6 difference

The name of the CommonJs AMD CMD ES6 Module
The full name CommonJs Asynchronous Module Definition Common Module Definition ECMAScript 6.0/2015
Synchronous/asynchronous synchronous asynchronous asynchronous
Implementation instance NodeJS RequireJS SeaJS (Taobao) JavaScript
Runtime environment The service side The browser The browser Before and after the end of the
The characteristics of 1. All code runs in module scope and does not pollute global scope;

2. The module is loaded synchronously, that is, the following operations can be performed only after the loading is completed;

3. The module will be cached after the first execution, and only the cached results will be returned when it is loaded again. If it wants to be executed again, the cache can be cleared;

4. CommonJS output is a copy of the value (i.e., the value returned by require is a copy of the value being output, and changes within the module do not affect this value).
Dependencies are front-loaded and executed ahead of time. indefineThe dependency modules (arrays) passed in the method are downloaded and executed at the start. Rely on proximity and delay execution. Only torequireIs executed by the dependent module.



❓ Sacrifice performance for ease of development?
CommonJS (contrast)

1. CommonJS Module is run time load, ES6 Module is compile time output interface;

ES6 Module can load an interface separately. ES6 Module can load an interface separately.

3. CommonJS output is a copy of the value, ES6 Module output is a reference to the value, the output Module internal changes will affect the reference change;

4. CommonJS this refers to the current Module, ES6 Module this refers to undefined;

5. Currently, browsers are not compatible with ES6 Module. Export /import used in webpack will be packaged as exports/require.

CommonJS

An overview of the

Each file is a module with its own scope. Variables, functions, and classes defined in one file are private and invisible to other files.

If you want to share variables across multiple files, you must define them as properties of the Global object.

global.warning = true;
Copy the code

The warning variable of the above code can be read by all files. Of course, this is not recommended.

Within each module, the module variable represents the current module. This variable is an object whose exports property (module.exports) is the interface to the outside world. Loading a module loads the module.exports property of that module.

var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
Copy the code

The code above exports the x variable and the function addX via module.exports.

The require method is used to load modules.

var example = require('./example.js');

console.log(example.x); / / 5
console.log(example.addX(1)); / / 6
Copy the code

Exports variable

For convenience, Node provides an exports variable for each module, pointing to module.exports. This equates to a line of command in the header of each module.

var exports = module.exports;
Copy the code

As a result, methods can be added to exports objects when exporting module interfaces.

exports.area = function (r) {
  return Math.PI * r * r;
};

exports.circumference = function (r) {
  return 2 * Math.PI * r;
};
Copy the code

Note that you can’t point the exports variable directly to a value, because that breaks the link between exports and module.exports.

exports = function(x) {console.log(x)};
Copy the code

Exports no longer refers to module.exports.

The following is also invalid.

exports.hello = function() {
  return 'hello';
};

module.exports = 'Hello world';
Copy the code

The hello function cannot be exported because module.exports is reassigned.

This means that if a module’s external interface is a single value, it cannot use exports, only module.exports.

module.exports = function (x){ console.log(x); };Copy the code

If you find it hard to distinguish between exports and module.exports, a simple solution is to stop using exports and just use module.exports.

Compatibility between AMD specification and CommonJS specification

The CommonJS specification loads modules synchronously, meaning that subsequent operations cannot be performed until the load is complete.

The AMD specification loads modules asynchronously and allows you to specify callback functions.

Node.js is mainly used for server programming, and module files usually already exist on the local hard disk, so it can be loaded quickly without considering the asynchronous loading mode, so the CommonJS specification is suitable.

However, in the browser environment, to load modules from the server side, then must be in asynchronous mode, so the browser side generally uses the AMD specification.

The AMD specification uses the define method to define modules. Here is an example:

define(['package/lib'].function(lib){
  function foo(){
    lib.log('hello world! ');
  }

  return {
    foo: foo
  };
});
Copy the code

The AMD specification allows the output module to be compatible with the CommonJS specification. The define method should be written as follows:

define(function (require.exports.module){
  var someModule = require("someModule");
  var anotherModule = require("anotherModule");

  someModule.doTehAwesome();
  anotherModule.doMoarAwesome();

  exports.asplode = function (){
    someModule.doTehAwesome();
    anotherModule.doMoarAwesome();
  };
});
Copy the code

The require command

Ruan Yifeng knows hu

Require is a copy of the exported value. That is, once a value is exported, changes within the module do not affect that value.

AMD (Asynchronous Module Definition)

The usage of require.js — Ruan Yifeng

AMD specification implementation:

<script src="require.js"></script>
<script src="a.js"></script>
Copy the code

So any other files imported after require.js can use define to define the module.

define(id? , dependencies? , factory)Copy the code

Id: An optional parameter used to define the identity of a module. If this parameter is not provided, use the JS file name (without the extension). This parameter can be omitted if only one module is defined for a JS file.

Dependencies: An optional parameter. It is an array of dependencies of the current module.

Factory: A factory method that a module initializes a function or object to execute. If it is a function, it should be executed only once, returning the value that the module exports. If it is an object, this object should be the output value of the module.

Module A can be defined as follows:

// a.js
define(function(){
    var name = 'morrain'
    var age = 18
    return {
        name,
        getAge: () = > age
    }
})
// b.js
define(['a.js'].function(a){
    var name = 'lilei'
    var age = 15
    console.log(a.name) // 'morrain'
    console.log(a.getAge()) / / 18
    return {
        name,
        getAge: () = > age
    }
})
Copy the code

It loads modules asynchronously without affecting the execution of subsequent statements. All statements that depend on this module are defined in the callback function, which will not run until the load is complete.

The basic idea of RequireJS is to define code as a module through the define method. When this module is required, it starts loading its dependent modules, and when all dependent modules are loaded, it starts executing the callback function that returns the exported value of the module. AMD stands for “Asynchronous Module Definition”.

CMD (Common Module Definition)

Like AMD, CMD is the normalized output of the module definition in the promotion process of Sea-js.

Sea.js was written by Ali’s Jade uncle. It was born after RequireJS, and Yub felt that the AMD specification was asynchronous and the organization of modules was not natural and intuitive. So he pursued a writing form like CommonJS. So you have CMD.

The implementation of the CMD specification:

<script src="sea.js"></script>
<script src="a.js"></script>
Copy the code

Any other files imported after the sea.js file can use define to define the module.

// All modules are defined with define
define(function(require.exports.module) {

  // introduce dependencies via require
  var a = require('xxx')
  var b = require('yyy')

  // Provides interfaces through exports
  exports.doSomething = ...

  // The entire interface is available through module.exports
  module.exports = ...

})
// a.js
define(function(require.exports.module){
    var name = 'morrain'
    var age = 18

    exports.name = name
    exports.getAge = () = > age
})
// b.js
define(function(require.exports.module){
    var name = 'lilei'
    var age = 15
    var a = require('a.js')

    console.log(a.name) // 'morrain'
    console.log(a.getAge()) / / 18

    exports.name = name
    exports.getAge = () = > age
})
Copy the code

The secret of sea-js being able to write module code synchronously like CommonsJS is that when b.js modules arerequireWhen b.js is loaded, sea-js will scan b.js code and findrequireThis keyword extracts all dependencies and loads them. When all dependencies are loaded, the callback function is executedrequire('a.js')At the time of this line of code, A.js is already loaded into memory.

ES6 Module

Two issues that must be considered in any modularity are import dependencies and export interfaces. The same is true for the ES6 Module, which consists of two commands: export and import. The export command is used to export the external interface of a module, and the import command is used to import the content exported from other modules.

Please refer to Teacher Ruan Yifeng’s tutorial for specific grammar explanation. Examples are as follows:

// a.js

export const name = 'morrain'
const age = 18
export function getAge () {
    return age
}

/ / equivalent to the
const name = 'morrain'
const age = 18
function getAge (){
    return age
}
export {
    name,
    getAge
}
Copy the code

Once the module’s external interface is defined using the export command, other JavaScript files can load the module with the import command.

// b.js
import { name, getAge } from 'a.js'
export const name = 'lilei'
console.log(name) // 'morrain'
const age = getAge()
console.log(age) / / 18

/ / equivalent to the
import * as a from 'a.js'
export const name = 'lilei'
console.log(a.name) // 'morrin'
const age = a.getAge()
console.log(age) / / 18
Copy the code

In addition to specifying that an output value is loaded, you can also use global loading, where you specify an object with an asterisk (*) on which all output values are loaded.

As you can see from the example above, when using the import command, the user needs to know the name of the variable to be imported, which is sometimes cumbersome, so the ES6 Module provides a convenient use by using the export default command to specify the default output for the Module.

// a.js
const name = 'morrain'
const age = 18
function getAge () {
    return age
}
export default {
    name,
    getAge
}

// b.js
import a from 'a.js'
console.log(a.name) // 'morrin'
const age = a.getAge()
console.log(age) / / 18
Copy the code

Obviously, a module can have only one default output, so the export default command can only be used once. As you can see, the curly braces are no longer needed after the import command.

In addition to the basic syntax, there are also as usage, export and import compound writing, export * from ‘a’, import() dynamic loading and other contents, you can learn by yourself.

📝❓ The previously mentioned Node.js supports ES6 Modules by default, and browsers fully support ES6 Modules. You can learn how to use ES6 Modules in Node.js and browsers.

ES6 Module and CommonJS

Too long, not to see here 🔎

Dynamic and static

The fundamental difference between CommonJS and ES6 modules is that the former resolves Module dependencies “dynamically” while the latter is “static”.

By “dynamic,” I mean that module dependencies are established at runtime; “Static” is when module dependencies are established during code compilation.

Value copying and dynamic mapping

When importing a module, CommonJS gets a copy of the exported value;

In the ES6 Module, it is a dynamic mapping of values, and the mapping is read-only.

www.cnblogs.com/tandaxia/p/…

Reference:

  1. Similarities and differences between AMD-CMD-CommonJS

  2. Understand import, require, export, module.export

  3. A Brief History of Programming series: The history of JavaScript modularization

  4. Javascript modular Programming (II) : AMD specification — Ruan Yifeng

  5. What are the differences between AMD and CMD? “– Answered The Jade uncle

  6. Similarities and differences with RequireJS

  7. CMD module defines the specification

  8. CommonJS — Wikipedia

  9. CommonJS specification — Ruan Yifeng

  10. CommonJS: Not the front end revolutionizes the front end

  11. Again, the differences between AMD, CMD, CommonJS and ES6 modules are sorted out