preface

As we all know, Javascript is not a modular programming language, there is no class class and Module concept, before NodeJs, there is no modular front-end, only modular back-end, with the increasing complexity of front-end projects, front-end code for modular demand. Front-end developers have been pushing a series of solutions on modular development, and later language level support

The old way of writing it

1. Encapsulate functions

/ / module A
function aFun(){
//to do something 
}
B / / modules
function bFun(){
//to do something 
}
Copy the code

Disadvantages: Multiple modules before the relationship is not obvious, easy to produce variable naming conflicts

2. Object encapsulation

var module1 = new Object({_count : 0.m1 : function (){/ /...},m2 : function (){/ /...}});Copy the code

Use:

module1.m1()
Copy the code

Disadvantages: Internal state can be overwritten externally, such as _ externally overwriting the count attribute directly

module1._count = 10
Copy the code

3. Self-executing functions

var module1 = (function($){var _count = 0;var m1 = function(){/ /...};var m2 = function(){/ /...};return{m1: m1,m2: m2}; })(jQuery);Copy the code

Closures are used to avoid exposing private members, and dependencies between modules can be managed by passing in parameters, or libraries of dependencies

The emergence of CommonJS, AMD and CMD

NodeJS is a major practitioner of the CommonJS specification because it interacts with operating systems and other applications. NodeJS has four important environment variables that support modular implementations: Module, exports, require, global. Module. exports defines the interface for the current module’s exports (not recommended), and requires loads the module.

// Define the module math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { // Write functions and variables that need to be exposed here
  add: add,
  basicNum: basicNum
}

/** the./ path must be added, otherwise only **/ will be found in node_modules
// When referencing a custom module, the argument contains the path and can omit.js
var math = require('./math');
math.add(2.5);

// No path is required when referencing core modules
var http = require('http'); http.createService(...) .listen(3000);
Copy the code

Exports and module.export

Exports: a variable (object) in itself. It is not a reference to module. It is a reference to {}, which points to the {} module of module.exports. Only. Syntax can be used to expose variables externally.

Module. exports: Module is a variable that points to a block of memory. Exports is a property in a module that is stored in memory, and exports is a property that points to {} modules. You can either use the. Syntax, or you can assign directly using =.

CommonJS loads modules synchronously. On the server side, the module files are stored on local disk and are very fast to read, so this should not be a problem. However, on the browser side, it is more reasonable to use asynchronous loading for network reasons.

AMD and require. Js

The AMD specification uses asynchronous loading of modules, which does not affect the execution of subsequent statements. All statements that depend on this module are defined in a callback function that will not run until the load is complete. Here’s how to implement the MODULarization of the AMD specification with require.js: specify reference paths, etc., define modules with definde(), and load modules with require().

First we need to import the require.js file and an entry file main.js. Configure require.config() in main.js and specify the base module to use in the project.

/** 网页中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js entry file/main module **/
// First specify the module path and reference name with config()
require.config({
  baseUrl: "js/lib".paths: {
    "jquery": "jquery.min".// The actual path is js/lib/jquery.min.js
    "underscore": "underscore.min",}});// Perform basic operations
require(["jquery"."underscore"].function($, _){
  // some code here
});
Copy the code

When referencing modules, we place the module name in [] as the first argument to reqiure(); If we define modules that themselves depend on other modules, we need to place them in [] as the first argument to define().

// Define the math.js module
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});

// Define a module that depends on glo.js
define(['underscore'].function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young'; })};return {
    classify :classify
  };
})

// Reference the module, put the module in []
require(['jquery'.'math'].function($, math){
  var sum = math.add(10.20);
  $("#sum").html(sum);
});
Copy the code

CMD and sea. Js

AMD’s implementer require.js will load and execute the code in the module as soon as it declares the dependent module:

define(["a"."b"."c"."d"."e"."f"].function(a, b, c, d, e, f) { 
    // all modules are declared and initialized first
    if (false) {
      // Module B is executed ahead of time even if it is not used. ** This is where CMD is optimized **
      b.foo()
    } 
});
Copy the code

CMD is another JS modularization solution, which is similar to AMD, except that AMD advocates relying on front-loading and up-front execution, while CMD advocates relying on nearby and delayed execution. This specification was actually created during the promotion of Sea-js.

/** AMD **/
define(["a"."b"."c"."d"."e"."f"].function(a, b, c, d, e, f) { 
     // all modules are declared and initialized first
    a.doSomething();
    if (false) {
        // Module B is executed ahead of time even if it is not used
        b.doSomething()
    } 
});
/** CMD **/
define(function(require.exports.module) {
    var a = require('./a'); // If required
    a.doSomething();
    if (false) {
        var b = require('./b'); b.doSomething(); }});/** sea.js **/
// Define the module math.js
define(function(require.exports.module) {
    var$=require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});

// Load the module
seajs.use(['math.js'].function(math){
    var sum = math.add(1+2);
});
Copy the code

ES6 Module

ES6, on the level of language standards, implements module functions, and implements them quite simply, aiming to become a common module solution for browsers and servers. Its module functions are mainly composed of two commands: export and import. The export command is used to specify the external interface of a module, and the import command is used to input functions provided by other modules.

/** Define the module math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** references the module **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}
Copy the code

You can also import all of them using AS

import * as all from './math';
function test(ele) {
    ele.textContent = all.add(99 + all.basicNum);
}
Copy the code

As shown in the example above, when using the import command, the user needs to know the name of the variable or function to be loaded. In fact, ES6 also provides the export default command, which specifies the default output of the module. The corresponding import statement does not need curly braces. This is also closer to ADM’s citation style.

/** export default **/
// Define the output
export default { basicNum, add };

/ / introduction
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}
Copy the code

ES6 modules are not objects, and the import command is statically analyzed by the JavaScript engine. The module code is introduced at compile time, rather than loaded at run time, so conditional loading cannot be implemented. Because of this, static analysis is possible.

Features of ES6 module:

  • Strict mode: ES6 modules automatically adopt strict mode
  • Import read-only Property: The import property is read-only and cannot be assigned, similar to const property
  • Export /import promotion: import/export must be at the top of the module, not in scope; Secondly, import/export within the module will be promoted to the top of the module, which is completed during compilation

Differences between ES6 modules and CommonJS modules

The 1.CommonJS module outputs a copy of a value, the ES6 module outputs a reference to a value

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. ES6 modules operate differently from CommonJS. When the JS engine statically analyzes a script, it generates a read-only reference to 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. In other words, the IMPORT of ES6 is a bit like the “symbolic link” of Unix systems, where the original value changes and the import load value changes with it. Therefore, ES6 modules are referenced dynamically and do not cache values. Variables in modules are bound to the module in which they are located.

  1. The CommonJS module is run time loaded, and the ES6 module is compile time output interface

Runtime loading: 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.” Compile-time loading: ES6 modules are not objects, but are output code explicitly specified through the export command and static commands when importing. 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.” Changes to references within a module are reflected externally. CommonJS loads an object (that is, the module.exports property) that is generated only after the script runs. An ES6 module is not an object, and its external interface is a static definition that is generated during the code static parsing phase.

Without further ado, let’s look at the code:

Let’s start with an example of CommonJS output copy:

// a.js
let a = 1;
let b = { num: 1 }
setTimeout(() = > {
    a = 2;
    b = { num: 2 };
}, 200);
module.exports = {
    a,
    b,
};

// main.js
// node main.js
let {a, b} = require('./a');
console.log(a);  / / 1
console.log(b);  // { num: 1 }
setTimeout(() = > {
    console.log(a);  / / 1
    console.log(b);  // { num: 1 }
}, 500);
Copy the code

Output copy, if you know NodeJS or WebPack’s implementation of CommonJS (see this article if you don’t) : Exports object is the only association between module and module. CommonJS output is the property of exports object. The property is determined when the module runs.

Look again at an example of ES6 Module output:

// a.mjs
let a = 1;
let b = { num: 1 }
setTimeout(() = > {
    a = 2;
    b = { num: 2 };
}, 200);
export {
    a,
    b,
};

// main.mjs
// node --experimental-modules main.mjs
import {a, b} from './a';
console.log(a);  / / 1
console.log(b);  // { num: 1 }
setTimeout(() = > {
    console.log(a);  / / 2
    console.log(b);  // { num: 2 }
}, 500);
Copy the code

This is the difference between ES6 Module output references and CommonJS output values. It is the ES6 Module specification that changes to internal references are reflected externally.

conclusion

AMD/CMD/CommonJs is js modular development specification, the realization of the corresponding is the require of js/sea. Js/Node. Js

CommonJs is for the server, AMD/CMD/ES Module is for the browser, and confusing is AMD/CMD. (By the way, what’s the essential difference between targeting the server side and the browser side? The server side usually uses synchronous load files, which means that when a module is needed, the server side stops and waits for it to load. If there are other back-end languages, such as Java. The browser side to ensure efficiency, need to use asynchronous loading, which requires a pre-processing, in advance of the required module files loaded in parallel.)

AMD/CMD difference, although are parallel loading JS files, but there is a difference, AMD is preloading, in parallel loading JS files at the same time, will also parse the execution of the module (because it also needs to be executed, so before loading a module, the module dependent module needs to be loaded first); CMD, on the other hand, is lazy, loading js files in parallel at the beginning, but not executed, but only executed when needed.

CommonJS and ES Module

The CommonJS module prints a copy of the value, the ES6 module prints a reference to the value

How to use it?

CommonJs, because NodeJS is its implementation, node is fine and you don’t need to import any other packages. AMD uses the