天行健, 君子以自强不息
Sunny's Blog
Title

ES6 Demos(十三) Encapsulating Code With Modules(用模块封装代码)

建议还可以看下如下这篇:JavaScript模块化方式:AMD, CommonJS和ES Harmony

这篇文章主要讲的就是ES Harmony(ES6)的模块化方法

这里面要补充一个重要内容(可能是一个意思,只是对动态的理解不一样): AMD可以动态的进行导入

                define(function ( require ) {
                    var isReady = false, foobar;

                    // note the inline require within our module definition
                    require(["foo", "bar"], function ( foo, bar ) {
                        isReady = true;
                        foobar = foo() + bar();
                    });

                    // we can still return a module
                    return {
                        isReady: isReady,
                        foobar: foobar
                    };
                });


                // export 与 import 都有一个重要的限制,那就是它们必须被用在其他语句或表达式的外部。
                // 不能动态导入导出绑定

                if (flag) {
                    export flag; // 语法错误
                }

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

                // 其实我觉得ES6也可以做如下操作: 这个和AMD可以动态的进行导入不是说的一个意思么(再理解)

                import { sum } from "./example.js";
                import { magicNumber } from "./example.js";
                console.log(sum(1, magicNumber)); // 8

                import { multiply } from "./example.js";
                console.log(multiply(1, 2)); // 2
            

Demo1: 何为模块

我之前的一篇文章对于模块化的理解是: 模块化是针对不同功能进行划分的一系列松耦合的模块,对于模块的标准,他至少符合如下三条:

1.封闭性,模块中定义的变量,函数,类不会自动添加到全局作用域中,这个就是不会进行全局变量污染

2.新模块开发可以依赖其他已有模块

3.新模块开发后可以支持其他模块的开发

Demo2: 基本的导出 export

   
                // 导出数据
                export var color = "red";
                export let name = "Nicholas";
                export const magicNumber = 7;

                //导出的函数声明与类声明必须要有名称。你不能使用这种语法来导出匿名函数或匿名类,
                //除非使用了 default 关键字

                // 导出函数
                export function sum(num1, num2) {
                    return num1 + num2;
                }

                // 导出类
                export class Rectangle {
                    constructor(length, width) {
                        this.length = length;
                        this.width = width;
                    }
                }

                // 此函数为模块私有
                function subtract(num1, num2) {
                    return num1 - num2;
                }

                // 定义一个函数……
                function multiply(num1, num2) {
                    return num1 * num2;
                }

                // ……稍后将其导出
                export { multiply };            
            

Demo3: 基本的导入 import

   
                //导入语句的基本形式
                import { 需要导入的标识符 } from "需导入的标识符的来源模块";
                import { identifier1, identifier2 } from "./example.js";
                //导入绑定的列表看起来与对象解构相似,但实则并无关联


                //注意你如果在chrome下测试要将script的type="module" 而不是用"text/javascript"

                // 导入单个绑定
                import { sum } from "./example.js";
                //要确保在导入的文件名前面使用 / 、 ./ 或 ../ ,以便在浏览器与 Node.js 之间保持良好兼容性。

                console.log(sum(1, 2)); // 3

                sum = 1; // error: Uncaught TypeError: Assignment to constant variable
                //当从模块导入了一个绑定时,该绑定表现得就像使用了 const 的定义。
                //这意味着你不能再定义另一个同名变量(包括导入另一个同名绑定),
                //也不能在对应的 import 语句之前使用此标识符(也就是要受暂时性死区限制),更不能修改它的值。


                // 导入多个绑定
                import { sum, multiply, magicNumber } from "./example.js";

                console.log(sum(1, magicNumber)); // 8

                console.log(multiply(1, 2)); // 2


                // 完全导入一个模块
                import * as example from "./example.js";
                // 这种导入格式被称为命名空间导入( namespace import ),
                // 这是因为该 example 对象并不存在于 example.js 文件中,
                // 而是作为一个命名空间对象被创建使用,其中包含了example.js 的所有导出成员。
                console.log(example.sum(1, example.magicNumber)); // 8

                console.log(example.multiply(1, 2)); // 2


                // 无论你对同一个模块使用了多少次 import 语句,该模块都只会被执行一次

                import { sum } from "./example.js";
                import { multiply } from "./example.js";
                import { magicNumber } from "./example.js";
                // 尽管此处的模块使用了三个 import 语句,但 example.js 只会被执行一次。


                //导入绑定的一个微妙怪异点

                //假设你想要使用以下模块
                export var name = "Nicholas";
                export function setName(newName) {
                    name = newName;
                }

                import { name, setName } from "./example.js";

                console.log(name); // "Nicholas"

                setName("Greg");
                //调用 setName("Greg") 会回到导出 setName() 的模块内部,并在那里执行,从而将 name 设置为 "Greg" 。
                console.log(name); // "Greg"

                name = "Nicholas"; // error: Uncaught TypeError: Assignment to constant variable.            
            

Demo4: 重命名导出与导入

   
                export { sum as add }; //导出重命名
                import { add as sum } from "./example.js"; //导入重命名
            

Demo5: 模块的默认值

   
                // 模块的默认值(default value )是使用 default 关键字所指定的单个变量、函数或类,
                // 而你在每个模块中只能设置一个默认导出,将 default 关键字用于多个导出会是语法错误。

                //导出默认值
                1
                export default function(num1, num2) {
                    return num1 + num2;
                }

                2
                function sum(num1, num2) {
                    return num1 + num2;
                }

                export default sum;

                3
                function sum(num1, num2) {
                    return num1 + num2;
                }

                export { sum as default };


                //导入默认值
                import sum from "./example.js";
                //注意此处并未使用花括号

                import sum, { color } from "./example.js";
                // 逗号将默认的本地名称与非默认的名称分隔开,后者仍旧被花括号所包裹。
                // 要记住在 import语句中默认名称必须位于非默认名称之前。

                import { default as sum, color } from "example";            
            

Demo6: 绑定的再导出

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

Demo7: 无绑定的导入

诸如 Array 与 Object 之类的内置对象的共享定义在模块内部是可访问的,并且对于这些对象的修改会反映到其他模块中。

比如你在example中对Array.prototype.pushAll定义了一个方法,你就不需要导出,直接:

   
              import "./example.js";            
            

Demo8: 加载模块

   
                <script type="module" src="module.js"></script>
                <script type="module"> 总是表现得像是已经应用了 defer属性。
            

defer 属性是加载脚本文件时的可选项,但在加载模块文件时总是自动应用的。

async 属性也能同样被应用到模块上。在 <script type="module"> 上使用 async 会导致模块的执行行为与脚本相似

地势坤,君子以厚德载物