Initial address: mp.weixin.qq.com/s/Be-hUjPbt…

Use the most concise way to grasp the core knowledge points to help you read ES6 quickly.

Key words: ESModule, CommonJs

【 Key points 】 (2)

The previous section covered class instantiation and inheritance

The text starts from here

preface

Before we start the text, let’s add some basic knowledge:

Q1: JS is interpreted execution, the execution process of each code block: parsing, precompilation stage, interpreted execution, where precompilation stage does some processing on variables and functions:

  • Function and variable declarations are promoted to the top of the function;

  • For variable assignment, it will first open up a memory space and point to the variable name, and assign the value of undefined;

  • For function declaration, the function body will be directly processed into the open memory space, that is, function declaration way, in the pre-compilation stage has completed the creation of the function.

**Q2: ** Why export and import can only be in the outermost module scope, why not in conditional statements?

Because import and export are analyzed during the static analysis phase, conditional statements are not resolved until execution.

**Q3: Exploration history of front-end modularization:

  • Functions: pollute global variables, naming conflicts;

  • Namespaces (simple object encapsulation) : The namespace is too long; Internal data can be directly modified, unsafe;

  • Execute functions immediately (closures) : private data, providing exposed interfaces; Support for incoming dependencies; (Modular prototype)

  • Problems to be solved: file dependency management, script introduction sequence, on-demand loading and other issues;

  • Server: commonJs; The browser: AMD (pre) rely on | CMD (rear) rely on

  • ESModule

ES Module

ES6 is designed to be as static as possible so that module dependencies, as well as input and output variables, can be determined at compile time.

export

The dynamic binding relationship between the interface name and the internal variables of the module is established for the external interface of the module.

1. Name the export

  • Export export const e1 = 18; Import {e1, e2} from ‘./my-module’;

  • Export value types are supported: functions, objects, or raw values

  • Allows exporting a list of the form: export {e1, e2}; Export {name: 123} export {name: 123} export {name: 123}

2. Export by default

  • There can be only one default interface;

  • Export format: export default [value of default]; Note that the value after default cannot be a variable declaration statement, because it corresponds to the exported interface name default, and the value after default is an assignment to it.

– Export interface variables with names: import _ from ‘./my-module’; You can call it whatever you want. The advantage is that the user does not need to check the API, can customize the variable use.

A mixture of 3.

The module also supports exporting interfaces of other modules:

// The default of module A is ignored, and the following line needs to be addedexport * from  './a'; // Note that this cannot be writtenexport default from './a', parsing error [from'./a'] should be a value that can be assigned to default. Is equivalent toexport { default } from './a'; // If you want to export the default of A, add the following code import default from'./a';
export { default };
Copy the code

import

The compile phase (static analysis), which is promoted to the module header, generates a read-only reference to the corresponding export interface in the module.

1. The imported variable name cannot use expressions or variables because it is executed statically.

2. The imported module is in singleton mode. Therefore, the same module is imported multiple times and executed only once.

3. Load as a whole. Use an asterisk (*) to specify an object to which the named and default exported interfaces will be mounted;

4. Default must be declared first

import _ , { fun } from './my-module.js';
Copy the code

5. Side effect import

import './my-module.js'; // when using webpack to do treeSharking, you can use sideEffects to declare any sideEffectsCopy the code

6. The imported reference is read-only:

The value cannot be reassigned, but if the imported object is an object, its properties can be modified.

import { var1, obj } from './module'; // Scenario 1:let var1 = 123; //Identifier 'var1'Has already been declared // scenario 2: var1 = 123; //Uncaught ReferenceError: var1 is not defined // scene 3: obj.a = 123; / / correct; You can modify, add, or delete an exported object interfaceCopy the code

Note when writing:

import _, * as M from 'moudle'; // scenario 2 m.fault = 123; // scenario 2 m.fault = 123; / / correct; And _ also becomes 123 m.var1 = 123; //Uncaught TypeError: Cannotset property var1 of #<Object> which has only a getter
Copy the code

Va1 has the following attribute descriptors:

Generally, variables in exported modules are const, object.freeze (), or immutable to avoid modification. Compare the three:

Const: block-level scope; Variables cannot be reassigned, but cannot be prevented from changing the properties of objects. 【 useletObject. Freeze: The Object cannot be modified. Shallow limit, if the object is multiple layers, can only be frozen recursively; Immutable: Each change returns a new object, shared structure.Copy the code

import()

Load the module dynamically and return a Promise object.

conclusion

1. To solve the naming conflicts, support with the as keyword rename export | import

2. The code in the module is executed in the module scope and bound to the module in which it resides, so variables in the external environment cannot be used;

3. Strict mode is used by default in the module;

4. At the top of the module, this keyword returns undefined. It can be used to determine whether the current ES6 module is in use.

Here is a general schematic to help you understand (source network, intrusion)

The key is to associate the exported variable with the corresponding memory address in the parsing stage. Assignment is at run time (declarative function special understanding).

Circular dependencies

//a.js
import { age } from './b.js'console.log(age); / / 18export const name= 'M2';
export function getName() {/ /... } //b.js import { name, getName } from'./a';
console.log(name);  //undefined
console.log(getName);  //f getName() {}
export const age = 18;

//main.js
import 'a.js'
Copy the code

When executed, b.js is normally retrieved, but variables are not. Reason: In the static analysis stage, getName and Name interfaces are linked in memory. Since getName function has the promotion function, it has been defined before the import execution of A. Js. Therefore, when the getName interface is used in B. Js, it will be linked to the corresponding memory, so it can be obtained and used.

commonJs

1. Runtime load, export module objects (module.exports);

2. Value copy.

Attached is a simple implementation of node module loading:

let path = require('path');
let fs = require('fs');
let vm = require('vm');

functionModule(filename){ // ... this.filename = filename; this.exports = {}; // The module exports the object this.loaded =false; } module. _cache = {}; // Cache module. _extentions = ['.js'.'.json'.'node']; // Find the Module's full file path module.findabspath =function(filename) {
    const p = path.resolve(_dirname, filename);
    if(! Path.extname (p)) {// Whether to include suffix const extentions = module._extentions; /* * step1: * Take x as a file and search in order of x, x.js, x.son and x.ode. If found, return * step2: Json, x/index.js, x/index.json, and x/index.nodefor(let i = 0; i < extentions.length; i++) {
            letnewPath = p + extentions[i]; try { fs.accessSync(newPath); // Whether the file path is validreturnnewPath; }catch(e) {// catch exception}}}return p;
}

Module.wrap = function(script) {// wraps modules as functions, exports, etc., passed in as argumentsreturn` (function(exports,require,module,_dirname,_filename){
        ${script}} `; } // Different types of files correspond to different processing logic module._extentions ['js'] = function(module) {
    letscriptStr = fs.readFileSync(module.filename); ler fnStr = Module.wrap(scriptStr); // vm.runInThisContext(fnStr) is similarevalExports, req, module); // Exports, exports, req, module; // Exports, exports, req, module; // Exports, exports, req, module. } // Load the Module module.prototype.load =function(filename) {
    let ext = path.extname(filename).slice(1);
    Module._extentions[ext](this);
}

function_require(filename) {// Get the absolute path of the fileletabsName = Module.findAbsPath(filename); / / determine whether loaded | singleton patternlet cacheModule = Module._cache[absName];
    if(cacheModule) {
        returncacheModule.exports; } // instantiate the module objectletmodule = new Module(absName); // Core modules are stored in nativemodule. cache // file modules are stored in module. cache // exports are mounted with what they are. {}】 module. _cache[filename] = Module; // synchronously load module.load(filename); // The module flag is loaded. Module.loaded =true; // Return the modulereturn module.exports;
}
Copy the code

Hot update memory leak

// The file Module is cached in the cache property of require: require.cache = module. cache // Remove the cache, and reload the Module on each read, to implement the hot update loading configuration. delete require.cache[require.resolve('./xx')]
Copy the code

But because parent. Children. Push (this); The reference to the Module object is saved, and the reference to the newly loaded module is pushed again each time the module is reloaded, causing a memory leak.

Circular dependencies

Load-time. Once a module is “lofted”, only the part that has been executed (the value of the current Module object being mounted) is output, not the part that has not been executed.

Node supports ES6 modules

[Option 1] : Node12.10 supports ESModule. Later versions have issues, but the latest version will support it again (github.com/CesiumGS/ce…

.mjs files are always loaded as ES6 modules. CJS files are always loaded as CommonJS modules. The loading of.js files depends on package.jsontypeField Settings.Copy the code

[Option 2] : Tripartite support

  1. Es6 transcoding modules are commonJS modules: rollup, gulp + Babel

  2. Module._extentions[ext], the core of which is to hijack the source file, process it into CommonJs using specific logic, and finally return the rewritten file:

const newCode = hook(code, filename); AddHook ((code, filename) => code.replace()'@@foo'.'console.log(\'foo\'); '), 
    { exts: ['.js'], matcher } ); 2. Babel-register: rewrite the require command to add a hook to it. Whenever files with.js,.jsx,.es, and.es6 suffixes are loaded with Babel first. require('@babel/register')({
  plugins: ['@babel/plugin-transform-modules-commonjs']}); require('./index'); 3. Babel-node: provides a convenient way to run es6 module files directly from the command line."scripts": {
      "run": "babel-node src/index.js --plugins @babel/plugin-transform-modules-commonjs"}}Copy the code

review

1. Introduces the principle of ESModule, the method of use and matters needing attention;

2. The CommonJs module loading and Node support ESModule scheme are summarized.

3. In addition, AMD is a front-end asynchronous acquisition module, the implementation of the callback function solution: obtaining module, by means of dynamic create scripts to load immediately after the completion of execution (CMD is the cache first download a file, such as real executed) when using object caching module, only all depend on the execution of the current module to perform the current module.

We will soon write an article on the principle of link loading (Runtime) of WebPack module