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 相关文章推荐
两个Javascript小tip资料
Nov 23 Javascript
jquery formValidator插件ajax验证 内容不做任何修改再离开提示错误的bug解决方法
Jan 04 Javascript
目前流行的JavaScript库的介绍及对比
Sep 29 Javascript
JavaScript实现实时更新系统时间的实例代码
Apr 04 Javascript
Vue.js tab实现选项卡切换
May 16 Javascript
JS简单添加元素新节点的方法示例
Feb 10 Javascript
微信小程序网络请求封装示例
Jul 24 Javascript
微信小程序自定义select下拉选项框组件的实现代码
Aug 28 Javascript
vue forEach循环数组拿到自己想要的数据方法
Sep 21 Javascript
JavaScript面试技巧之数组的一些不low操作
Mar 22 Javascript
微信小程序网络请求实现过程解析
Nov 06 Javascript
JS组件库AlloyTouch实现图片轮播过程解析
May 29 Javascript
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中die(),exit(),return的区别
2013/06/20 PHP
php使用base64加密解密图片示例分享
2014/01/20 PHP
php字符比较函数similar_text、strnatcmp与strcasecmp用法分析
2014/11/18 PHP
php缓冲输出实例分析
2015/01/05 PHP
详解PHP实现执行定时任务
2015/12/21 PHP
thinkphp5 URL和路由的功能详解与实例
2017/12/26 PHP
PHP count_chars()函数讲解
2019/02/14 PHP
javascript简易缓动插件(源码打包)
2012/02/16 Javascript
jquery 触发a链接点击事件解决方案
2013/05/02 Javascript
Express.JS使用详解
2014/07/17 Javascript
javascript记录文本框内文字个数检测文字个数变化
2014/10/14 Javascript
javascript获取select值的方法分析
2015/07/02 Javascript
javascript匀速运动实现方法分析
2016/01/08 Javascript
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
2016/08/29 Javascript
Bootstrap图片轮播组件Carousel使用方法详解
2016/10/20 Javascript
进阶之初探nodeJS
2017/01/24 NodeJs
angular中的http拦截器Interceptors的实现
2017/02/21 Javascript
jQuery扇形定时器插件pietimer使用方法详解
2017/07/18 jQuery
详解win7 cmd执行vue不是内部命令的解决方法
2017/07/27 Javascript
nodejs+express搭建多人聊天室步骤
2018/02/12 NodeJs
vue实现在线预览pdf文件和下载(pdf.js)
2019/11/26 Javascript
微信小程序自定义纯净模态框(弹出框)的实例代码
2020/03/09 Javascript
python将人民币转换大写的脚本代码
2013/02/10 Python
深入Python解释器理解Python中的字节码
2015/04/01 Python
解决Ubuntu pip 安装 mysql-python包出错的问题
2018/06/11 Python
python实现控制台打印的方法
2019/01/12 Python
详解Python用三种方式统计词频的方法
2019/07/29 Python
Python绘制动态水球图过程详解
2020/06/03 Python
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
《陈毅探母》教学反思
2014/05/01 职场文书
森林病虫害防治方案
2014/06/02 职场文书
2014年秋季开学寄语
2014/08/02 职场文书
2014七年级班主任工作总结
2014/12/05 职场文书
靠谱的活动总结
2019/04/16 职场文书
竞聘开场白方式有哪些?
2019/08/28 职场文书
jquery插件实现搜索历史
2021/04/24 jQuery