ES6新特性之模块Module用法详解


Posted in Javascript onApril 01, 2017

本文实例讲述了ES6新特性之模块Module用法。分享给大家供大家参考,具体如下:

一、Module简介

ES6的Class只是面向对象编程的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题。Module功能就是为了解决这个问题而提出的。

历史上,JavaScript一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能。

在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。ES6在语言规格的层面上,实现了模块功能,而且实现得相当简单,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。

ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系(这种加载称为“编译时加载”),以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。

浏览器使用ES6模块的语法如下。

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

上面代码在网页中插入一个模块fs.js,由于type属性设为module,所以浏览器知道这是一个ES6模块。

// ES6加载模块
import { stat, exists, readFile } from 'fs';

上面代码通过import去加载一个Module,加载其中的一些方法。

二、import 和 export

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个JS文件,里面使用export命令输出变量。

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

export的写法,除了像上面这样,还有另外一种。(推荐这种,因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。)

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};

export命令除了输出变量,还可以输出函数或类(class)。通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

function v1() { ... }
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。

// main.js
import {firstName, lastName, year} from './profile';
function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

上面代码的import命令,就用于加载profile.js文件,并从中输入变量。import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

import { lastName as surname } from './profile';

import命令具有提升效果,会提升到整个模块的头部,首先执行。

foo();
import { foo } from 'my_module';

三、模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

有一个circle.js文件,它输出两个方法area和circumference。

现在,加载这个模块。

// main.js
import { area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));

上面写法是逐一指定要加载的方法,整体加载的写法如下。

import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

四、export default

为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js
export default function () {
  console.log('foo');
}

上面代码是一个模块文件export-default.js,它的默认输出是一个函数。

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'

需要注意的是,这时import命令后面,不使用大括号。

本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。它后面不能跟变量声明语句。

// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;

五、ES6模块加载的实质

ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。

CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个模块文件lib.js的例子。

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

上面代码输出内部变量counter和改写这个变量的内部方法incCounter。然后,在main.js里面加载这个模块。

// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3

上面代码说明,lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。

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

上面代码中,输出的counter属性实际上是一个取值器函数。现在再执行main.js,就可以正确读取内部变量counter的变动了。

ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

还是举上面的例子。

// lib.js
export let counter = 3;
export function incCounter() {
 counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

上面代码说明,ES6模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。

由于ES6输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。

// lib.js
export let obj = {};
// main.js
import { obj } from './lib';
obj.prop = 123; // OK
obj = {}; // TypeError

上面代码中,main.js从lib.js输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。

最后,export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。

// mod.js
function 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.js
import {c} from './mod';
c.add();
// y.js
import {c} from './mod';
c.show();
// main.js
import './x';
import './y';

现在执行main.js,输出的是1。这就证明了x.js和y.js加载的都是C的同一个实例。

希望本文所述对大家ECMAScript程序设计有所帮助。

Javascript 相关文章推荐
express的中间件bodyParser详解
Dec 04 Javascript
简要了解jQuery移动web开发的响应式布局设计
Dec 04 Javascript
基于JavaScript实现轮播图代码
Jul 14 Javascript
AngularJS入门教程之Helloworld示例
Dec 25 Javascript
jQuery发请求传输中文参数乱码问题的解决方案
May 22 jQuery
微信小程序实现自定义modal弹窗封装的方法
Jun 15 Javascript
vue生命周期实例小结
Aug 15 Javascript
webpack4与babel配合使es6代码可运行于低版本浏览器的方法
Oct 12 Javascript
layer.msg()去掉默认时间,实现手动关闭的方法
Sep 12 Javascript
对vue中的事件穿透与禁止穿透实例详解
Oct 28 Javascript
node.js中module模块的功能理解与用法实例分析
Feb 14 Javascript
如何vue使用el-table遍历循环表头和表体数据
Apr 26 Vue.js
Vue.js实战之组件之间的数据传递
Apr 01 #Javascript
ES6新特性之解构、参数、模块和记号用法示例
Apr 01 #Javascript
jQuery UI Grid 模态框中的表格实例代码
Apr 01 #jQuery
前端自动化开发之Node.js的环境搭建教程
Apr 01 #Javascript
ES6新特性之数组、Math和扩展操作符用法示例
Apr 01 #Javascript
手机端转换rem适应
Apr 01 #Javascript
ES6新特性之变量和字符串用法示例
Apr 01 #Javascript
You might like
PHP 时间转换Unix时间戳代码
2010/01/22 PHP
php INI配置文件的解析实现分析
2011/01/04 PHP
修改php.ini以达到屏蔽错误信息并记录日志
2013/06/16 PHP
解析PHP跨站刷票的实现代码
2013/06/18 PHP
PHP实现更新中间关联表数据的两种方法
2014/09/01 PHP
php使用类继承解决代码重复的问题
2015/02/11 PHP
php反射学习之不用new方法实例化类操作示例
2019/06/14 PHP
浅谈laravel-admin的sortable和orderby使用问题
2019/10/03 PHP
一个网马的tips实现分析
2010/11/28 Javascript
jQuery bind事件使用详解
2011/05/05 Javascript
jQuery1.6 使用方法二
2011/11/23 Javascript
JS函数实现动态添加CSS样式表文件
2012/12/15 Javascript
JS基于MSClass和setInterval实现ajax定时采集信息并滚动显示的方法
2016/04/18 Javascript
Web Uploader文件上传插件使用详解
2016/05/10 Javascript
JS 通过系统时间限定动态添加 select option的实例代码
2016/06/09 Javascript
Node.js批量给图片加水印的方法
2016/11/15 Javascript
移动端日期插件Mobiscroll.js使用详解
2016/12/19 Javascript
angular仿支付宝密码框输入效果
2017/03/25 Javascript
vue文件树组件使用详解
2018/03/29 Javascript
微信小程序实现上传图片功能
2018/05/28 Javascript
element-ui表格列金额显示两位小数的方法
2018/08/24 Javascript
vue框架制作购物车小球动画效果实例代码
2019/09/26 Javascript
再也不怕 JavaScript 报错了,怎么看怎么处理都在这儿
2020/12/09 Javascript
用python 批量更改图像尺寸到统一大小的方法
2018/03/31 Python
Python输入二维数组方法
2018/04/13 Python
python爬虫实例详解
2018/06/19 Python
如何基于python实现不邻接植花
2020/05/01 Python
CSS实现聊天气泡效果
2020/04/26 HTML / CSS
如何给HTML标签中的文本设置修饰线
2019/11/18 HTML / CSS
在职研究生自我鉴定
2013/10/16 职场文书
大学活动总结格式
2014/04/29 职场文书
给学校的建议书范文
2014/05/15 职场文书
四风自我剖析材料
2014/09/30 职场文书
单独二胎证明
2015/06/24 职场文书
基于python的matplotlib制作双Y轴图
2021/04/20 Python
Vue如何实现组件间通信
2021/05/15 Vue.js