当前位置:K88软件开发文章中心编程语言JavaScriptECMAScript → 文章内容

ECMAScript 6 Module

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 15:38:43

efault class { ... }// main.jsimport MyClass from 'MyClass';let o = new MyClass();模块的继承模块之间也可以继承。假设有一个circleplus模块,继承了circle模块。// circleplus.jsexport * from 'circle';export var e = 2.71828182846;export default function(x) { return Math.exp(x);}上面代码中的export *,表示再输出circle模块的所有属性和方法。注意,export *命令会忽略circle模块的default方法。然后,上面代码又输出了自定义的e变量和默认方法。这时,也可以将circle的属性或方法,改名后再输出。// circleplus.jsexport { area as circleArea } from 'circle';上面代码表示,只输出circle模块的area方法,且将其改名为circleArea。加载上面模块的写法如下。// main.jsimport * as math from 'circleplus';import exp from 'circleplus';console.log(exp(math.e));上面代码中的import exp表示,将circleplus模块的默认方法加载为exp方法。ES6模块加载的实质ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个模块文件lib.js的例子。// lib.jsvar counter = 3;function incCounter() { counter++;}module.exports = { counter: counter, incCounter: incCounter,};上面代码输出内部变量counter和改写这个变量的内部方法incCounter。然后,在main.js里面加载这个模块。// main.jsvar mod = require('./lib');console.log(mod.counter); // 3mod.incCounter();console.log(mod.counter); // 3上面代码说明,lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。// lib.jsvar counter = 3;function incCounter() { counter++;}module.exports = { get counter() { return counter }, incCounter: incCounter,};上面代码中,输出的counter属性实际上是一个取值器函数。现在再执行main.js,就可以正确读取内部变量counter的变动了。$ node main.js34ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。还是举上面的例子。// lib.jsexport let counter = 3;export function incCounter() { counter++;}// main.jsimport { counter, incCounter } from './lib';console.log(counter); // 3incCounter();console.log(counter); // 4上面代码说明,ES6模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。再举一个出现在export一节中的例子。// m1.jsexport var foo = 'bar';setTimeout(() => foo = 'baz', 500);// m2.jsimport {foo} from './m1.js';console.log(foo);setTimeout(() => console.log(foo), 500);上面代码中,m1.js的变量foo,在刚加载时等于bar,过了500毫秒,又变为等于baz。让我们看看,m2.js能否正确读取这个变化。$ babel-node m2.jsbarbaz上面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。由于ES6输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。// lib.jsexport let obj = {};// main.jsimport { obj } from './lib';obj.prop = 123; // OKobj = {}; // TypeError上面代码中,main.js从lib.js输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。最后,export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。// mod.jsfunction C() { this.sum = 0; this.add = function () { this.sum += 1; }; this.show = function () { console.log(this.sum); };}export let c = new C();上面的脚本mod.js,输出的是一个C的实例。不同的脚本加载这个模块,得到的都是同一个实例。// x.jsimport {c} from './mod';c.add();// y.jsimport {c} from './mod';c.show();// main.jsimport './x';import './y';现在执行main.js,输出的是1。$ babel-node main.js1这就证明了x.js和y.js加载的都是C的同一个实例。循环加载“循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。// a.jsvar b = require('b');// b.jsvar a = require('a');通常,“循环加载”表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行,因此应该避免出现。但是实际上,这是很难避免的,尤其是依赖关系复杂的大项目,很容易出现a依赖b,b依赖c,c又依赖a这样的情况。这意味着,模块加载机制必须考虑“循环加载”的情况。对于JavaScript语言来说,目前最常见的两种模块格式CommonJS和ES6,处理“循环加载”的方法是不一样的,返回的结果也不一样。CommonJS模块的加载原理介绍ES6如何处理"循环加载"之前,先介绍目前最流行的CommonJS模块格式的加载原理。CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。{ id: '...', exports: { ... }, loaded: true, ...}上面代码就是Node内部加载模块后生成的一个对象。该对象的id属性是模块名,exports属性是模块输出的各个接口,loaded属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。以后需要用到这个模块的时候,就会到exports属性上面取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。CommonJS模块的循环加载CommonJS模块的重要特

上一页  [1] [2] [3] [4] [5]  下一页


ECMAScript 6 Module