Module 模块化笔记

以下是对模块的以下简单梳理,关键字:ES6 Module, CommonJS

script标签对模块的支持

<script type="module" src="module/mod.js"></script>

加载效果类似于

<script type="javascript" defer></script>

加载顺序如输出结果

<script type="text/javascript">
    console.log('before mod load')
</script>

<script type="text/javascript" defer src="script_defer.js"></script>

<script type="module" src="module.js"></script>

<script type="text/javascript">
    console.log('after mod load')
</script>

输出
before mod load
after mod load
deferred script loaded
mod loaded

script-module 标签与 script-defer 标签同属异步加载,在页面渲染成功后按顺序加载。

但是,script-module与script-defer 在作用域上有很大不同。
– 代码是在模块作用域之中运行,而不是在全局作用域中运行。模块内部的顶层变量是外部不可见的。
– 模块脚本自动采用严格模式,无论有没有声明use strict。
– 模块之中可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对URL或相对URL),也可以使用export命令输出对外接口。
– 在模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字是无意义的。
– 同一个模块如果加载多次,将只执行一次。

ES6模块与CommonJS模块的差异

  • CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。
  • CommonJS模块是运行时加载,ES6模块是编译时输出接口。
  • CommonJS模块的顶层this指向当前模块,ES6模块之中,顶层的this指向undefined。

对于ES6 Module

<script>
        // 假设存在module.js,  mod: {a:1}
    import mod from "./module.js";
    console.log(mod);  // 输出 {a:1}
    mod.a = 2;
    console.log(mod);  // 输出 {a:2}

        // 二次加载模块并不会重新执行模块
    import * as anothermod from "./module.js";
    console.log(anothermod.default.a);  // 输出 2
</script>

而对于CommonJS

// module.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

使用该模块

// main.js
var mod = require('./lib');

console.log(mod.counter);   // 3
mod.incCounter();
console.log(mod.counter);   // 3

mod.counter的值并没有改变,究其原因,mod.counter 被转化成一个字面值
incCounter 会带来模块内部的变化,但不会影响到 counter

var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};

如果要获取模块内的变化,可以通过构建访问器实现(以上代码)。
访问器(set/get) 语法实现了用函数绑对象的属性,当属性被查询或者设置时,绑定函数会被调用。

以下引用自参考
ES6模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令import就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用到被加载的模块中取值。换句话说,ES6的import有点像Unix系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

以上描述的内容都可以在浏览器环境下进行验证(当前版本Chrome 60)
如果执行失败,需要关注浏览器版本。Chrome版本状态

区分环境

NodeJS环境与浏览器环境的差异,其中一个方面表现在标准的支持程度。
NodeJS出现的那天,module遵循CommonJS规范并使用require()方法加载,在新版本NodeJS发布之前,Node并不支持import(当前node.js 版本 v8)。

值得一提:import与require区别。

从思想上,ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

  • 有人提议了import()方法来实现动态加载,满足按需加载的功能。
  • import()与import命令不冲突。我个人对import()非常支持。
  • import(“./mod.js”); // 返回 Promise对象 demo

发表评论

电子邮件地址不会被公开。 必填项已用*标注