16.11.11node
过了又一个关棍节,感受。。。。很差es6
参考:阮一峰ES6缓存
CommonJS对模块的定义很是简单,主要分为模块引用、模块定义和模块标志babel
var math = reqiure('math');
require()接受模块的标志符。以此引入一个模块的API到当前的上下文中。函数
上下文提供exports
对象用于导出当前模块的方法和变量,而且它是惟一导出的出口。module
对象,它表明模块自身。而exports是module的属性。ui
模块标识就是传递给require()方法的参数。必须符合小驼峰命名的字符串。设计
CommonJS的模块导出和引用机制使得用户彻底没必要考虑变量污染。code
ES6模块的设计思想,是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时肯定这些东西。对象
// CommonJS模块 let { stat, exists, readFile } = require('fs'); // 等同于 这种方式称为“运行时加载” let _fs = require('fs'); let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;
exports
&& module.exports
继承
直接复制给exports,会报错。
exports= function(){ // }
由于exports对象是经过形参的方式传入的,直接赋值形参会改变形参的做用,但并不能改变做用域外的值。
若是要达到require引入一个类的效果,请赋值给module.exports对象,这个迂回的方案不改变形参的引用。
须要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量创建一一对应关系。
使用export命令定义了模块的对外接口之后,其余JS文件就能够经过import命令加载这个模块(文件)。
import命令接受一个对象(用大括号表示),里面指定要从其余模块导入的变量名。大括号里面的变量名,必须与被导入模块s对外接口的名称相同。
除了指定加载某个输出值,还可使用总体加载,即用星号(*)指定一个对象,全部输出值都加载在这个对象上面。
export default 一个模块只有一个。为模块指定默认输出。
使用import命令的时候,用户须要知道所要加载的变量名或函数名,不然没法加载。可是,用户确定但愿快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
其余模块加载该模块时,import命令能够为该匿名函数指定任意名字。
如下比较默认输出和正常输出:
// 输出 export default function crc32() { // ... } // 输入 import crc32 from 'crc32'; // 输出 export function crc32() { // ... }; // 输入 import {crc32} from 'crc32';
本质上,export default就是输出一个叫作default的变量或方法,而后系统容许你为它取任意名字。
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块彻底不一样。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不同,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的须要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号链接”,原始值变了,import输入的值也会跟着变。所以,ES6模块是动态引用,而且不会缓存值,模块里面的变量绑定其所在的模块。
CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,而后在内存生成一个对象。
{ id: '...', exports: { ... }, loaded: true, ... }
该对象的id属性是模块名,exports属性是模块输出的各个接口,loaded属性是一个布尔值,表示该模块的脚本是否执行完毕。
之后须要用到这个模块的时候,就会到exports属性上面取值。即便再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。也就是说,CommonJS模块不管加载多少次,都只会在第一次加载时运行一次,之后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
<mark>CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会所有执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。</mark>
// a.js exports.done = false; var b = require('./b.js'); console.log('在 a.js 之中,b.done = %j', b.done); exports.done = true; console.log('a.js 执行完毕'); //b.js exports.done = false; var a = require('./a.js'); console.log('在 b.js 之中,a.done = %j', a.done); exports.done = true; console.log('b.js 执行完毕'); //main.js var a = require('./a.js'); var b = require('./b.js'); console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done); //执行main.js $ node main.js 在 b.js 之中,a.done = false b.js 执行完毕 在 a.js 之中,b.done = true a.js 执行完毕 在 main.js 之中, a.done=true, b.done=true
ES6处理“循环加载”与CommonJS有本质的不一样。ES6模块是动态引用,若是使用import从一个模块加载变量(即import foo from 'foo'),那些变量不会被缓存,而是成为一个指向被加载模块的引用,须要开发者本身保证,真正取值的时候可以取到值。
// a.js以下 import {bar} from './b.js'; console.log('a.js'); console.log(bar); export let foo = 'foo'; // b.js import {foo} from './a.js'; console.log('b.js'); console.log(foo); export let bar = 'bar'; $ babel-node a.js b.js undefined a.js bar
上面说过,const声明的常量只在当前代码块有效。若是想设置跨模块的常量(即跨多个文件),能够采用下面的写法。
// constants.js 模块 export const A = 1; export const B = 3; export const C = 4; // test1.js 模块 import * as constants from './constants'; console.log(constants.A); // 1 console.log(constants.B); // 3 // test2.js 模块 import {A, B} from './constants'; console.log(A); // 1 console.log(B); // 3