Reading notes: in-depth understanding of ES6 (13)

Chapter 13 Encapsulating Code with Modules

  Other languages ​​use concepts such as packages to define the scope of code. Before ES6, Javascript used a "share everything" method to load code, and everything defined shares a global scope. As Web applications become more complex, the amount of code becomes larger. Increase, this approach will cause naming conflicts, security issues, etc.

  One of the goals of ES6 is to solve the scope problem, and in order to make the program appear orderly, so modules are introduced.

 

Section 1 What is a module?

  1. Definition of the module

   The module is automatically run in strict mode, and there is no way to exit the running JavaScript code.

  

  2. Features of the module

    a) Variables created at the top of the module only exist in the top-level scope of the module and will not be automatically added to the global shared scope.

    b) The module must export some externally accessible elements, such as variables and functions.

    c) At the top of the module, the value of this is undefined.

    d) The module does not support HTML style code comments.

 

  3. The difference between modules and scripts

    a) A script is any JavaScript code that is not a module.

    b) Scripts and modules represent an important change in JavaScript code loading and evaluation.

 

Section 2 Basic grammar derived

  1. You can use the export keyword to expose a part of the published code to other modules, that is, put export in front of any variable, function or class declaration to export them from the module.

  2. Except for the export keyword, every declaration is exactly the same as in the script. Unless the default keyword is used, this syntax cannot be used to export anonymous function classes.

  3. Any variables, functions and classes that are not explicitly exported are private to the module and cannot be accessed from outside the module.

 

Section 3 Basic Syntax of Import

  1. The functions exported from the module can be accessed in another module through the import keyword.

 

  2. The two parts of the import statement are: the identifier to be imported and which module the identifier should be imported from. For example:

 import { identifier1, identifier2 } from "./example.js";

  

  3. The braces after import indicate the binding imported from a given module.

   When importing a binding from a module, it is as if it were defined using cons t. The result is that you cannot define another variable with the same name (including importing another binding with the same name), nor can you use an identifier or change the value of the binding before the import statement.

 

  4. You can import a single binding or multiple bindings. Under special circumstances, you can import the entire module as a single object, and then all exports can be used as attributes of the object. This format of importing the entire module is called namespace import.

 

  5. An important limitation of export and import is that they must be used outside of other statements and functions. For example:

 if (flag)
 {
     export flag; //语法错误
 }
 
 //another example
 function tryImport() {
     import flag from "./example.js"; // 语法错误
 }

 

Section 4 Rename when exporting and importing

  Sometimes, in the process of exporting and importing, you may not want to use their original names. At this time, you can use the as keyword to achieve this requirement. For example:

 //导出的模块代码
 function sum (sum1, sum2)
 {
     return num1 + num2;
 } 
 
 export {sum as add}
 
 //导入的模块代码
 import { add } from "./example.js"
 
 //也可以这样导入
 import {add as sum } from "./example.js"

 

Section 5 Module Default Values

  1. The default value of a module refers to a single variable, function or class specified by the default keyword. Only one default export value can be set for each module. Using the default keyword multiple times during export is a syntax error. Give an example of exporting default values:

 export default function(num1, num2) {
     return num1 + num2;
 }

 

  2. Since default is the default keyword in Javascript, it cannot be used as the name of a variable, function or class, but it can be used as a property name.

 

  3. For modules that export default values ​​and one or more non-default bindings, you can use one statement to import all exported bindings. At this time, you need to separate the default local name from the non-default value enclosed in braces with a comma. Remember, in the import statement, the default value must be sorted before the non-default value.

    For detailed code see P.321-P.323

 

Section 6 Re-export a binding

  1. In some scenarios, it may be used to re-export the imported values. E.g:

 import { sum } from "./example.js";
 export { sum }

  

  2. Although the above code can run, the same task can be accomplished with only one statement:

 export { sum } from "./example.js"

  This form of export looks for the sum statement in the specified module, and then exports it.

 

  3. The same value can also be exported with different names:

 export { sum as add } from "./example.js"

 

  4. You can use "*" to export all values ​​in another module. Exporting everything means exporting default values ​​and all named export values, which may affect the content exported from the module. For example: if exmple.js has a default export value, you will not be able to define a new default export when using this syntax.

 

Section 7 Import without binding

  1. Although the top-level variables, functions and classes in the module will not automatically appear in the global scope, this does not mean that the module cannot access the global scope. Shared definitions of built-in objects (such as Array and Object) can be accessed in the module, and changes made to these objects will be reflected in other modules. E.g:

 //没有export或者import的代码
 Array.prototype.pushAll() = function(items)
 {
     //items必须是一个数组
     if (!Array.isArray(items)) 
     {
         throw new TypeError("参数必须是一个数组");
     }
 
     //使用内建的push()和展开运算符
     return this.push(...items);
 }

  

  2. Even if there is no export or import operation, the above example is a valid module. This code can be used as a module or as a script.

 

  3. Since this kind of module is neither exported nor imported, a more concise import operation syntax can be used to execute the module code, and no binding is required to be imported. E.g:

 import "./example.js";
 
 let colors = ["red", "green", "blue"];
 let items = [];
 
 items.pushAll(colors);

  

  4. Unbound import is most likely to be used to create polyfill and shim.

 

Section 8 Load Module

  ES6 defines the syntax of modules, but does not define how to load these modules. This is a manifestation of the complexity of the specification, which should be determined by different implementation environments (for example: Web browser and Node.js environment)

 

  1. Use the module in a web browser.

    There are three methods:

    1) In the <script> element, load the JavaScript code file through the src attribute.

    2) The JavaScript code is embedded in the <script> element without the src attribute.

    3) Load JavaScript code files through Web worker or Service Worker.

 

  2. Module loading sequence in the web browser:

   Among them, the modules are executed in the order in which they appear in the HTML file. For example:

 <!-- 先执行这个标签 -->
 <script type="module" src="module1.js"></script>
     
 <!-- 再执行这个标签 -->
 <script type="module" src="module2.js">
 import {sum} from "./example.js"
 
 let result = sum(1,2);
 </script>
 
 <!-- 最后执行这个标签 -->
 <script type="module" src="module2.js"></script>

    In this example, the module is first parsed to identify all import statements; then, each import statement triggers an acquisition process, and the current module will be executed after all imported resources have been loaded and executed. The complete loading sequence is as follows:

    1. Download and parse module1.js.

    2. Recursively download and parse the imported resources in module1.js.

    3. Resolve inline modules.

    4. Recursively download and parse the imported resources in the inline module.

    5. Download and parse module2.js.

    6. Recursively download and parse the imported resources in module2.js.

 

  After loading, other operations will be performed only after the document is completely parsed. After the document is parsed, the following actions will occur:

    1. Recursively execute the resources imported in module1.js.

    2. Execute module1.js.

    3. Recursively execute the imported resources in the inline module.

    4. Execute the inline module.

    5. Recursively execute the imported resources in module2.js.

    6. Execute module2.js.

 

  3. Asynchronous module loading in the web browser

    There is an async attribute on the <script> element. The order of the async scripts in the document will not affect the order of script execution. The script will be executed immediately after the download is complete, without waiting for the included document to finish parsing.

    The async attribute can also be used on modules, which are executed in a similar way to scripts. The only difference is that all imported resources in the module must be downloaded before the module is executed.

 

  4. Load the module as a Worker

    Workers, such as Web workers or Service workers, can execute JavaScript code outside of the context of the web page. Steps to create Worker: create an instance + pass in the address of the JavaScript file. E.g:

 //按照脚本的方式加载script.js
 let worker = new Worker("script.js");

    

    In order to support loading modules, the developers of the HTML standard added a second parameter to these constructors. The second parameter is an object whose type attribute defaults to "script". You can set the type to module to load the module file. E.g:

 //按照模块的方式加载module.js
 let worker = new Worker("module.js", { type: "module" });

 

  5. Browser module specifier parsing

    1) The module specifier (module specifier) ​​in the previous examples are all relative paths (such as the string "./example.js"). The browser requires the module specifier to have one of the following formats:

      ·The analysis starting with / starts from the root directory.

      ·The analysis beginning with ./ starts from the current directory.

      ·The analysis beginning with ../ starts from the parent directory.

      ·URL format.

      For example, suppose there is a module file located at https://www.example.com/modules/module.js, which contains the following code:

 // 从 https://www.example.com/modules/example.js 导入
 import { first } from "./example.js";
 
 // 从 https://www.example.com/example2.js 导入
 import { second } from "../example.js";
 
 // 从 https://www.example.com/example3.js 导入
 import { third } from "/example.js";
 
 // 从 https://www.example.com/example4.js 导入
 import { fourth } from "https://www.example.com/example4.js";

 

    2) The module specifier is slightly different between the <script> tag and the import when referring to the module. This difference is intentional. E.g,

 //无效的,没有以 /、 ./ 或 ../ 开头
 import { first } from "example.js"
 
 //无效的,没有以 /、 ./ 或 ../ 开头
 import {second} from "example/index.js"

 

 

(End of this section)

Guess you like

Origin blog.csdn.net/ZxxSteven/article/details/100851183