Cover: © Michael j. Kochniss | MJK – photo. DE | instagram.com/mjk_photo

In the early days, JavaScript programs were mainly used to achieve some animation or simple interaction on the page, so the program would not be too complex, the page would not have too much JavaScript code, and there was no need for modular development of the front-end in JavaScript programs. However, with the increase of the complexity of applications, in order to enhance the maintainability of code and improve the loading performance of applications, the front-end development of modular JavaScript development is increasingly demanding. This article will walk you through the evolution of JavaScript modularity development by looking at the different modularity solutions that have been adopted in the community.

What is modularity

Modularization is a method to separate the system into independent functional parts. It can divide the system into independent functional parts, strictly define module interfaces, and have transparency between modules. To put it simply, when we develop applications, we usually divide them into different modules according to different functions, and then combine them into a whole in a specific way to finally complete the development of the whole application system functions. The modular development of JavaScript can help us solve the problems of global variable pollution, function naming conflict, dependency and code reuse in the development process.

Modularless development

The most original JavaScript program development is without the concept of modularity, in a page, no matter how many functions, how much code to write, we write the code into one or several JS files, and then the page through the script tag to introduce the relevant JS files. Function when the page is complicated, or need different developers together to develop the same functionality, will inevitably encounter global variables, pollution and the problem of naming conflicts, moreover is very complex, write the amount of code that will be more from the outside into the page number of JS file more and more, Without managing and organizing the code and its dependencies, late maintenance can become very passive.

Execute functions now (IIFE)

To prevent global variable contamination and naming conflicts in JavaScript code, developers have introduced self-executing functions in JavaScript, the kind of closures that most people get asked about in job interviews. What is a closure? A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure. That is, closures allow you to access the scope of an outer function within an inner function. In JavaScript, whenever a function is created, the closure is created at the same time the function is created.

Through closure can form an independent scope, its declare variables only within this scope, outside the closure can’t access and manipulate, so as to achieve the purpose of a private variable, has solved the above mentioned global variables, pollution and the problem of naming conflicts, but inside the closure can through to expose some methods, To make variables inside the closure accessible from outside the closure. In addition, if the inside of a closure wants access to a global variable or to a variable in another module, it can be passed inside the closure by executing the function immediately.

( function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { module.exports = global.document ?  factory( global, true ) : function( w ) { if ( ! w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } } )( typeof window ! == "undefined" ? window : this, function( window, noGlobal ) { } );Copy the code

This closure function takes two line arguments. The first argument is when typeof Window! If == “undefined”, pass window as an argument, otherwise pass this as an argument, and the second argument is an anonymous function, so the use of closures to isolate variables is quite widespread.

The namespace

In addition to solving the problem of variable contamination, we can also use the namespace approach. In short, we can create a simple object literal to hold all related variables and functions, and use the object literal to simulate the effect of the namespace.

var NAMESPACE = { person: function(name) { this.name = name; this.getName = function() { return this.name; }}};Copy the code

CommonJS

The CommonJS specification loads modules synchronously, meaning that subsequent operations are not performed until the load is complete. Node.js is mainly used for server programming. Modules are stored in local hard disks and load quickly, so Node.js adopts the CommonJS specification.

The CommonJS specification is divided into three parts: module identification, require, and exports. The module variable, inside each module, represents the current module; The exports attribute is an external interface that exports methods or variables of the current module. Require () loads external modules, reads and executes js files, and returns the exports object of that module.

CommonJS has the following features:

  • All code runs in the module scope and does not pollute the global scope.
  • Modules can be loaded multiple times, but only run once on the first load, then run the results are cached, and read directly from the cache when loaded again. If you want the module to run again, you must clear the cache.
  • The order in which modules are loaded, in the order they appear in the code.

Define modules:

// config.js
var api = 'https://github.com/trending?since=weekly&_pjax=%23js-pjax-container';
var config = {
  api: api,
};
module.exports = config;
Copy the code

Reference module:

// utils.js var config = require('./config'); var utils = { request() { console.log(config.api); }}; module.exports = utils;Copy the code

AMD Modular Solution

As the front-end business evolves, the code becomes more complex, and organizing the code in a way that simply executes functions or namespaces is no longer appropriate. The front end needs to have a clearer and simpler way to handle the initial problems of global variable naming conflicts and dependencies between JS code. Different JS modular specifications have emerged in the community, among which the use of the more popular is AMD and CMD, first to introduce the AMD modular scheme.

AMD stands for “Asynchronous Module Definition”. It loads modules asynchronously without affecting 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. It is a specification for modular development on the browser side. Because it is not JavaScript native support, page development using AMD specification needs the corresponding library function (RequireJS library). In fact, AMD is the standardized output of RequireJS module definition in the promotion process.

AMD uses the require() statement to load the module, which requires two parameters:

require([module], callback);
Copy the code

The first argument, [module], is an array whose members are the modules to be loaded. The second argument, callback, is the callback function after the successful loading.

Define modules:

// a.js
define(function (){
  return {
   a:'hello world'
  }
});
Copy the code

Reference module:

// b.js require(['./a.js'], function (moduleA){ console.log(moduleA.a); // Print out: hello world});Copy the code

CMD modular scheme

CMD is the Common Module Definition, known as the Common Module loading specification. It is also used on the browser side. The browser-side asynchronous loading library sea-.js implements the CMD specification. It is similar to AMD, except that AMD relies on front-loading and up-front execution, while CMD relies on nearby and delayed execution.

Define modules:

define(function(require, exports, Var module2 = require('./module2') // Require. Async ('./module3', Function (m3) {}) // exports.xxx = value})Copy the code

Reference module:

define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.foo()
  m4.bar()
})
Copy the code

ES6 Module

In ES6 before the introduction of modular concept, in order to solve the problem of JS modular development, the community proposed CommonJS, AMD, CMD modular development program; ES6 modular draws advantages of CommonJS and AMD, concise syntax, supports asynchronous loading, aims to provide common modular solutions for browsers and servers, but in the long run, whether in the future in the Web end or based on Node server or desktop end, modular development specifications will be unified using ES6 Module.

Module definitions in ES6: Two new keywords export and import are added in ES6. Export is used to expose the contents of a module, and import is used to introduce functionality provided by a module.

// test.js let name = 'test.js '; function getName() { return name; } function setName(n) { name = n; } export { name, getName, setName, }Copy the code

Loading modules in ES6: Use the import syntax to load modules as follows:

import{ getName } from './test'
getName();
Copy the code

Note: You can use the export default command to specify the default output for the module. A module can only have one default output, so export default can only be used once.

ES6 modules are dynamic references. If you load a variable from a module with import (import foo from ‘foo’), the variable is not cached, but instead becomes a reference to the loaded module. When the script is executed, the value is evaluated in the loaded module based on a read-only reference.

The level is limited, the article inevitably has deficiencies, welcome everyone to pay attention to my wechat public number. (Front-end migrant worker)