"Sleeping suit" interviewer series Chapter 10 module Grammar (recommended Collection)

catalogue

1. general

2. strict mode

3. export command

4. import command

5. overall loading of modules

6. export default command

7. compound writing of export and import

8. module inheritance

9. cross module constant

10. import()

10.1 introduction

11 applicable occasions

11.1 on demand loading

11.2 conditional loading

11.3 dynamic module path

11.4 precautions

summary

Summary of all series of "nightwear" interviewer series (recommended to learn and collect)

1. general

Historically, JavaScript has never had a module system. It is impossible to split a large program into interdependent small files and then assemble them in a simple way. Other languages have this feature,
For example, Ruby's require, Python's import, and even CSS have @import, but JavaScript doesn't support any of this. This is very important for developing large and complex projects
A huge obstacle.

Before ES6, the community developed some module loading schemes, including CommonJS and AMD. The former is used for the server and the latter for the browser. ES6 at the level of language standards,
The module function has been realized, and the implementation is quite simple. It can completely replace CommonJS and AMD specifications and become a common module solution for browsers and servers.
The design idea of ES6 module is to be static as much as possible, so that the dependencies of the module and the input and output variables can be determined at compile time. CommonJS and AMD modules can only be confirmed at runtime
Order these things. For example, the CommonJS module is an object, and the object attributes must be found during input.

// CommonJS module
let { stat, exists, readFile } = require('fs');
// Equivalent to
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

The essence of the above code is to load the fs module as a whole (that is, to load all the methods of fs), generate an object (\u fs), and then read three methods from this object. This loading is called "runtime plus"
Because this object can only be obtained at runtime, there is no way to do "static optimization" at compile time.
The ES6 module is not an object, but the output code is explicitly specified by the export command, and then input by the import command.

// ES6 module
import { stat, exists, readFile } from 'fs';

The essence of the above code is to load three methods from the fs module, and other methods are not loaded. This kind of loading is called "compile time loading" or static loading, that is, ES6 can complete module loading at compile time, which is more efficient
It is higher than the loading mode of CommonJS module. Of course, this also makes it impossible to reference the ES6 module itself because it is not an object.
Because the ES6 module is loaded at compile time, static analysis is possible. With it, you can further broaden the syntax of JavaScript, such as introducing macro s and type checking
system) these functions can only be realized by static analysis.
In addition to the various benefits of static loading, the ES6 module has the following benefits.
UMD module format is no longer required. In the future, both servers and browsers will support ES6 module format. At present, this has been achieved through various tool libraries.
In the future, the browser's new API can be provided in module format, and it no longer has to be made into global variables or attributes of navigator objects.
Objects are no longer required as namespaces (such as Math objects). In the future, these functions can be provided through modules

2. strict mode

ES6 modules automatically adopt strict mode, whether you add "use strict" to the module header or not.
Strict mode mainly has the following limitations.
Variables must be declared before use
The parameter of a function cannot have an attribute with the same name, or an error will be reported
Cannot use with statement
You cannot assign a value to a read-only attribute, or an error will be reported
The prefix 0 cannot be used to represent octal numbers, otherwise an error will be reported
The undeleted attribute cannot be deleted, otherwise an error will be reported
The variable delete prop cannot be deleted, and an error will be reported. You can only delete the attribute delete global[prop]
eval does not introduce variables in its outer scope
eval and arguments cannot be reassigned
arguments does not automatically reflect changes in function parameters
Arguments Callee
Arguments Caller
Prohibit this from pointing to global objects
Cannot use fn Caller and fn Arguments get the stack of function calls
Added reserved words (such as protected, static, and interface)
The modules must comply with the above restrictions. Since the strict mode is introduced by ES5 and does not belong to ES6, please refer to the relevant ES5 books, which will not be introduced in detail.
Among them, we should pay special attention to the limitation of this. In the ES6 module, the top-level this points to undefined, that is, this should not be used in the top-level code.

3. export command

The module function is mainly composed of two commands: export and import. The export command is used to specify the external interface of the module, and the Import command is used to input the functions provided by other modules.
A module is an independent file. All internal variables of the file cannot be obtained externally. If you want the external to read a variable inside the module, you must use the export keyword to output the
Variables. The following is a JS file that uses the export command to output variables.

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

The above code is profile JS file, which saves the user information. ES6 regards it as a module, in which three variables are output externally with the export command.
Besides the above, there is another way to write export.

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};

The above code, after the export command, uses braces to specify a set of variables to be output. It is equivalent to the previous method (placed directly before the var statement), but it should be used first
Law. In this way, you can see what variables are output at a glance at the end of the script.
The export command can output functions or class es in addition to variables.

export function multiply(x, y) {
return x * y;
};

The above code outputs a function multiply.
Normally, the variable output from export is the original name, but it can be renamed with the as keyword.

function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};

The above code uses the as keyword to rename the external interfaces of functions v1 and v2. After renaming, v2 can be output twice with different names.
It should be noted that the export command specifies the external interface and must establish a one-to-one correspondence with the variables inside the module.

// Error reporting
export 1;
// Error reporting
var m = 1;
export m;

The above two methods will report errors because no external interface is provided. The first method outputs 1 directly. The second method outputs 1 directly through the variable m. 1 is only a value, not an interface. Correct
It is written as follows

// Writing method I
export var m = 1;
// Writing 2
var m = 1;
export {m};
// Writing method III
var n = 1;
export {n as m};

The above three methods are correct and specify the external interface m. Other scripts can get the value 1 through this interface. Their essence is to establish one-to-one correspondence between the interface name and the internal variables of the module
Relationship.
Similarly, the output of function and class must also follow this method

// Error reporting
function f() {}
export f;
// correct
export function f() {};
// correct
function f() {}
export {f};

In addition, the interface output by the export statement has a dynamic binding relationship with its corresponding value, that is, through this interface, you can get the real-time value inside the module

export var foo = 'bar';
setTimeout(() => foo = 'baz', 500)

The above code outputs the variable foo with a value of bar, which becomes baz after 500 milliseconds.
This is completely different from the CommonJS specification. The output of the CommonJS Module is the value cache, and there is no dynamic update. See the section "Module loading implementation" below for details.
Finally, the export command can appear anywhere in the module, as long as it is at the top level of the module. If it is within the block level scope, an error will be reported, as will the import command in the next section. This is because
In order to be in the conditional code block, it is impossible to perform static optimization, which violates the original design intention of the ES6 module

function foo() {
export default 'bar' // SyntaxError
}
foo()

In the above code, the export statement is placed in the function, and the result is an error.

4. import command

After defining the external interface of the module with the export command, other JS files can load the module with the import command.

// main.js
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}

The import command of the above code is used to load the profile JS file and enter variables from it. The import command accepts a pair of braces that specify the names of variables to be imported from other modules. Inside braces
The variable name of must be the same as the name of the external interface of the imported module (profile.js).
If you want to rename the input variable, the import command should use the as keyword to rename the input variable.

import { lastName as surname } from './profile';

The from after import specifies the location of the module file, which can be a relative path or an absolute path The js suffix can be omitted. If it is only a module name without a path, there must be a configuration file
File to tell the JavaScript engine the location of the module.

import {myMethod} from 'util'

In the above code, util is the module file name. Since there is no path, you must configure it to tell the engine how to get this module.
Note that the import command has a promotion effect. It will be promoted to the head of the entire module and executed first.

foo();
import { foo } from 'my_module';

The above code will not report an error, because the execution of import is earlier than the call of foo. The essence of this behavior is that the Import command is executed during the compilation phase, before the code is run.
Because import is statically executed, expressions and variables cannot be used. These syntax structures can only be obtained at run time.

// Error reporting
import { 'f' + 'oo' } from 'my_module';
// Error reporting
let module = 'my_module';
import { foo } from module;
// Error reporting
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}

The above three methods will report errors because they use expressions, variables and if structures. In the static analysis phase, these grammars cannot get values.
Finally, the import statement will execute the loaded module, so it can be written as follows.

import 'lodash';

The above code simply executes the lodash module, but does not enter any values.
If the same import statement is executed multiple times, it will be executed only once, not multiple times

import 'lodash';
import 'lodash';

The above code loads lodash twice, but executes it only once.

import { foo } from 'my_module';
import { bar } from 'my_module';
// Equivalent to
import { foo, bar } from 'my_module';

In the above code, foo and bar are loaded in two statements, but they correspond to the same my_module instance. That is, the import statement is a Singleton pattern.
At present, the require command of CommonJS module and the import command of ES6 module can be written in the same module through Babel transcoding, but it is better not to do so. because
import is executed in the static parsing phase, so it is the earliest execution in a module. The following code may not get the expected results.

require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';

5. overall loading of modules

In addition to specifying a certain output value to be loaded, you can also use overall loading, that is, an object is specified with an asterisk (*), and all output values are loaded on this object.
Here is a circle JS file, which outputs the two methods area and circumference.

// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}

Now, load the module

// main.js
import { area, circumference } from './circle';
console.log('Circular area:' + area(4));
console.log('Circumference:' + circumference(14));

The above method is to specify the methods to be loaded one by one. The overall loading method is as follows.

import * as circle from './circle';
console.log('Circular area:' + circle.area(4));
console.log('Circumference:' + circle.circumference(14));

Note that the object where the module is loaded as a whole (circle in the above example) should be statically analyzed, so runtime changes are not allowed. The following expressions are not allowed

import * as circle from './circle';
// The following two lines are not allowed
circle.foo = 'hello';
circle.area = function () {}

6. export default command

As can be seen from the previous example, when using the import command, the user needs to know the name of the variable or function to be loaded, otherwise it cannot be loaded. However, users certainly want to get started quickly and may not be willing to read
Read the document to understand the attributes and methods of the module.
To make it convenient for users to load modules without reading documents, you need to use the export default command to specify the default output for the module

// export-default.js
export default function () {
console.log('foo');
}

The above code is a module file export-default JS, whose default output is a function.
When other modules load the module, the import command can specify any name for the anonymous function.

// import-default.js
import customName from './export-default';
customName(); // 'foo'

The import command of the above code can point to export-default JS output method. In this case, you do not need to know the function name output by the original module. It should be noted that the import command
Do not use braces after the command.
The export default command can also be used before non anonymous functions

// export-default.js
export default function foo() {
console.log('foo');
}
// Or written as
function foo() {
console.log('foo');
}
export default foo;

In the above code, the function name foo of foo function is invalid outside the module. Loading is regarded as anonymous function loading.
Let's compare the default output with the normal output

// first group
export default function crc32() { // output
// ...
}
import crc32 from 'crc32'; // input
// Group 2
export function crc32() { // output
// ...
};
import {crc32} from 'crc32'; // input

The above code is written in two groups. The first group is that when export default is used, the corresponding import statement does not need to use braces; The second group is the corresponding import language when export default is not used
The sentence requires braces.
The export default command specifies the default output of the module. Obviously, a module can only have one default output, so the export default command can only be used once. Therefore, after the import command
There is no need to enlarge the parentheses, because it can only correspond to one method.
In essence, export default is to output a variable or method called default, and then the system allows you to give it any name. Therefore, the following expression is valid

// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// Equivalent to
// export default add;
// app.js
import { default as foo } from 'modules';
// Equivalent to
// import foo from 'modules';

Because the export default command only outputs a variable called default, it cannot be followed by a variable declaration statement

// correct
export var a = 1;
// correct
var a = 1;
export default a;
// error
export default var a = 1;

In the above code, export default a means to assign the value of variable a to variable default. Therefore, the last way of writing will report an error.
Similarly, the essence of export default is to assign the value after the command to the default variable and then default, so write a value directly after export default

// correct
export default 42;
// Error reporting
export 42;

In the above code, the error is reported in the last sentence because no external interface is specified, while the previous sentence specifies that the external interface is default.
With the export default command, it is very intuitive to input modules. Take the lodash module as an example.

import _ from 'lodash'

If you want to input the default method and other interfaces in an import statement, you can write it as follows
 

import _, { each, each as forEach } from 'lodash'

The export statement corresponding to the above code is as follows.

export default function (obj) {
// ···
}
export function each(obj, iterator, context) {
// ···
}
export { each as forEach };

The last line of the above code means that the forEach interface is exposed and points to the each interface by default, that is, forEach and each point to the same method.
export default can also be used to output classes.

// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();

7. compound writing of export and import

If the same module is input before output in a module, the import statement can be written together with the export statement.

export { foo, bar } from 'my_module';
import { foo, bar } from 'my_module';
export { foo, bar };

In the above code, the export and import statements can be combined into one line.
Module interface renaming and overall output can also be written in this way.

// Interface rename
export { foo as myFoo } from 'my_module';
// Overall output
export * from 'my_module

The default interface is written as follows.

export { default } from 'foo';

The writing method of changing the named interface to the default interface is as follows

export { es6 as default } from './someModule';
// Equivalent to
import { es6 } from './someModule';
export default es6;

Similarly, the default interface can be renamed as a named interface

export { default as es6 } from './someModule';

The following three import statements have no corresponding compound writing.

import * as someIdentifier from "someModule";
import someIdentifier from "someModule";
import someIdentifier, { namedIdentifier } from "someModule";

In order to achieve symmetry in form, there is now a proposal to add these three compound expressions

export * as someIdentifier from "someModule";
export someIdentifier from "someModule";
export someIdentifier, { namedIdentifier } from "someModule";

8. module inheritance

Modules can also be inherited.
Suppose you have a circleplus module that inherits the circle module.

// circleplus.js
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}

Export * in the above code represents all the attributes and methods of the re output circle module. Note that the export * command ignores the default method of the circle module. Then, the above code outputs
Custom e variables and default methods are provided.
At this time, you can also rename the attributes or methods of the circle and then output them.

// circleplus.js
export { area as circleArea } from 'circle';

The above code indicates that only the area method of the circle module is output, and it is renamed circleArea.
The above modules are written as follows.

// main.js
import * as math from 'circleplus';
import exp from 'circleplus';
console.log(exp(math.e));

The import exp ort in the above code means that the default method of the circleplus module is loaded as the exp method.

9. cross module constant

const declared constants are only valid in the current code block. If you want to set constants across modules (that is, across multiple files), or a value must be shared by multiple modules
Enjoy, you can use the following expression

// Constants JS module
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js module
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js module
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3

If there are many constants to be used, you can create a special constants directory, write various constants in different files, and save them in this directory.

// constants/db.js
export const db = {
url: 'http://my.couchdbserver.local:5984',
admin_username: 'admin',
admin_password: 'admin password'
};
// constants/user.js
export const users = ['root', 'admin', 'staff', 'ceo', 'chief', 'moderator'];

Then, merge the constants output from these files into index JS.

// constants/index.js
export {db} from './db';
export {users} from './users';

When using, directly load index JS is OK

// script.js
import {db, users} from './index'

10. import()

10.1 introduction

As mentioned earlier, the import command will be statically analyzed by the JavaScript engine and executed before other modules in the module (called "connection" is more appropriate). Therefore, the following code will report an error

// Error reporting
if (x === 2) {
import MyModual from './myModual';
}

In the above code, the engine processes the import statement during compilation, and will not analyze or execute the if statement at this time. Therefore, the import statement is meaningless in the if code block, so it will report a syntax error, and
Not an execution time error. That is, the import and export commands can only be at the top level of the module, not in the code block (for example, in the if code block or in the function).
Such a design, of course, helps the compiler to improve efficiency, but it also makes it impossible to load modules at run time. Syntactically, conditional loading is impossible. If the import command replaces the
Require method, which forms an obstacle. Because require is a runtime loading module, the import command cannot replace the dynamic loading function of require.

const path = './' + fileName;
const myModual = require(path);

The above statement is dynamic loading. Only the runtime knows which module to load. The import statement cannot do this.
Therefore, there is a proposal to introduce the import() function to complete dynamic loading.

import(specifier)

In the above code, the parameter specifier of the import function specifies the location of the module to be loaded. The Import command can accept any parameter, and the import() function can accept any parameter. The difference between the two is
The latter is mainly dynamic loading.
import() returns a Promise object. Here is an example

const main = document.querySelector('main');
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});

The import() function can be used anywhere, not only in modules, but also in non module scripts. It is run-time execution, that is, when it runs to this sentence, it will also load the specified module
Block. In addition, the import() function has no static connection with the loaded module, which is also different from the import statement.
import() is similar to the require method of Node. The main difference is that the former is asynchronous loading and the latter is synchronous loading.

11 applicable occasions

Here are some applications of import().

11.1 on demand loading

import() can load a module when necessary

button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});

In the above code, the import() method is placed in the listener function of the click event. This module will not be loaded until the user clicks the button.

11.2 conditional loading

import() can be placed in the if code block, and different modules can be loaded according to different situations.

if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}

In the above code, if the conditions are met, load module A; otherwise, load module B.

11.3 dynamic module path

import() allows dynamic generation of module paths.

import(f())
.then(...);

In the above code, different modules are loaded according to the return result of function f.

11.4 precautions

After import() successfully loads the module, the module will be used as an object and as a parameter of the then method. Therefore, you can use the syntax of object deconstruction assignment to obtain the output interface.

import('./myModule.js')
.then(({export1, export2}) => {
// ...·
});

In the above code, both export1 and export2 are mymodule JS output interface, which can be obtained by deconstruction.
If the module has a default output interface, it can be obtained directly with parameters.

import('./myModule.js')
.then(myModule => {
console.log(myModule.default);
});

The above code can also be in the form of named input.

import('./myModule.js')
.then(({default: theDefault}) => {
console.log(theDefault);
});

If you want to load multiple modules at the same time, you can use the following writing method.

Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});

import() can also be used in async functions

async function main() {
const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main()

summary

This blog comes from my reading of relevant books and video summaries. It is not easy to create. Thank you for your support. Learning is earning. I am a ballad, inspired to become an excellent technical innovation personnel.

Welcome to exchange private letters, learn and grow together.

It is recommended to link other file directory references

Summary of all series of "nightwear" interviewer series (recommended to learn and collect)

Tags: Front-end

Posted by jbbaxx on Mon, 30 May 2022 14:55:11 +0530