ES6新特性:使用export和import实现模块化详解


Posted in Javascript onJuly 31, 2017

在ES6前, 前端就使用RequireJS或者seaJS实现模块化, requireJS是基于AMD规范的模块化库,  而像seaJS是基于CMD规范的模块化库,  两者都是为了为了推广前端模块化的工具, 更多有关AMD和CMD的区别, 后面参考给了几个链接;

现在ES6自带了模块化, 也是JS第一次支持module, 在很久以后 ,我们可以直接作用import和export在浏览器中导入和导出各个模块了, 一个js文件代表一个js模块;

现代浏览器对模块(module)支持程度不同, 目前都是使用babelJS, 或者Traceur把ES6代码转化为兼容ES5版本的js代码;

ES6的模块化的基本规则或特点:

ES6的模块化的基本规则或特点, 欢迎补充:

1:每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;

2:每一个模块内声明的变量都是局部变量, 不会污染全局作用域;

3:模块内部的变量或者函数可以通过export导出;

4:一个模块可以导入别的模块

运行下面代码

//lib.js
//导出常量
export const sqrt = Math.sqrt;
//导出函数
export function square(x) {
  return x * x;
}
//导出函数
export function diag(x, y) {
  return sqrt(square(x) + square(y));
}

//main.js
import { square, diag } from './lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

ES6新特性:使用export和import实现模块化详解

下面列出几种import和export的基本语法:

第一种导出的方式:

在lib.js文件中, 使用 export{接口} 导出接口, 大括号中的接口名字为上面定义的变量, import和export是对应的;

运行下面代码

//lib.js 文件
let bar = "stringBar";
let foo = "stringFoo";
let fn0 = function() {
  console.log("fn0");
};
let fn1 = function() {
  console.log("fn1");
};
export{ bar , foo, fn0, fn1}

//main.js文件
import {bar,foo, fn0, fn1} from "./lib";
console.log(bar+"_"+foo);
fn0();
fn1();

ES6新特性:使用export和import实现模块化详解

第二种导出的方式:

在export接口的时候, 我们可以使用 XX as YY, 把导出的接口名字改了, 比如: closureFn as sayingFn, 把这些接口名字改成不看文档就知道干什么的:

运行下面代码

//lib.js文件
let fn0 = function() {
  console.log("fn0");
};
let obj0 = {}
export { fn0 as foo, obj0 as bar};

//main.js文件
import {foo, bar} from "./lib";
foo();
console.log(bar);

ES6新特性:使用export和import实现模块化详解

第三种导出的方式:

这种方式是直接在export的地方定义导出的函数,或者变量:

运行下面代码

//lib.js文件
export let foo = ()=> {console.log("fnFoo") ;return "foo"},bar = "stringBar";

//main.js文件
import {foo, bar} from "./lib";
console.log(foo());
console.log(bar);

ES6新特性:使用export和import实现模块化详解

第四种导出的方式:

这种导出的方式不需要知道变量的名字, 相当于是匿名的, 直接把开发的接口给export;

如果一个js模块文件就只有一个功能, 那么就可以使用export default导出;

运行下面代码

//lib.js
export default "string";

//main.js
import defaultString from "./lib";
console.log(defaultString);

ES6新特性:使用export和import实现模块化详解

第五种导出方式:

export也能默认导出函数, 在import的时候, 名字随便写, 因为每一个模块的默认接口就一个:

运行下面代码

//lib.js
let fn = () => "string";
export {fn as default};

//main.js
import defaultFn from "./lib";
console.log(defaultFn());

第六种导出方式:

使用通配符*  ,重新导出其他模块的接口 (其实就是转载文章, 然后不注明出处啦);

运行下面代码

//lib.js
export * from "./other";
//如果只想导出部分接口, 只要把接口名字列出来
//export {foo,fnFoo} from "./other";

//other.js
export let foo = "stringFoo", fnFoo = function() {console.log("fnFoo")};

//main.js
import {foo, fnFoo} from "./lib";
console.log(foo);
console.log(fnFoo());

ES6新特性:使用export和import实现模块化详解

其他:ES6的import和export提供相当多导入以及导出的语法;

在import的时候可以使用通配符*导入外部的模块:

运行下面代码

import * as obj from "./lib";
console.log(obj);

ES6导入的模块都是属于引用:

每一个导入的js模块都是活的, 每一次访问该模块的变量或者函数都是最新的, 这个是原生ES6模块 与AMD和CMD的区别之一,以下代码修改自http://exploringjs.com/es6/ch_modules.html#_imports-are-read-only-views-on-exports

运行下面代码

//lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}
export function setCounter(value) {
  counter = value;
}


//main.js
import { counter, incCounter ,setCounter} from './lib';

// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4
setCounter(0);
console.log(counter); // 0

在main.js中, counter一直指向lib.js中的局部变量counter, 按照JS的尿性, 像数字或者字符串类型或者布尔值的原始值要被复制, 而不是赋址;

循环依赖的问题:

NodeJS的循环依赖是这么处理的:打开;

循环依赖是JS模块化带来的问题, 在浏览器端, 使用RequireJS测试模块化, 比如有一个文件file0.js依赖于file1.js, 而file1.js又依赖于file0.js, 那么file0.js和file1.js到底谁先执行?

运行下面代码

//index.html
<!DOCTYPE html>
<html>
<head>
  <title></title>
  <meta charset="utf-8"/>
</head>
<body>

<script data-main="cyclic" src="//cdn.bootcss.com/require.js/2.2.0/require.min.js"></script>
<script>
//cyclic.js
require(["file0"], function(file0) {
  console.log(file0)
})

//file0.js
define(["file1"], function(file1) {
  console.log(file1)
  return {
    file0 : "file0"
  }
})

//file1.js
define(["file0"], function(file0) {
  console.log(file0);
  return {
    file1 : "file1"
  }
})
</script>
</body>
</html>

在控制台的依次输出为:

运行下面代码

undefined
Object { file1: "file1" }
Object { file0: "file0" }

在执行file1.js的时候file0.js还没执行完, 所以输出了undefined, 这种输出结果和NodeJS输出的情况是一样的;

然后我又使用了司徒大神的mass-framework框架试了一下, 司徒大神的框架直接提示我: "模块与之前的某些模块存在循环依赖", 这样还比较好点, requireJS对于循环依赖是直接执行循环依赖的模块, 会导致在开发的时候给自己挖坑....;

接下来我又在babel-node下进行测试:下面是几个测试,可以无视:

我使用ES6的模块试一试, 只要每一个模块被引用, 无论模块是否执行完毕, 该模块的export已经被导出了, 如果导出的是函数:

运行下面代码 

//cyclic.js
import fn0 from "./file0";
fn0();

//file0.js
import fn1 from "./file1";
fn1();
console.log("file0.js runs");
export default function() {console.log("file0 export runs")}

//file1.js
import fn0 from "./file0";
fn0();
console.log("file1.js runs");
export default function() {console.log("file1 export runs")}

ES6新特性:使用export和import实现模块化详解

如果导出的是字符串:

运行下面代码

//cyclic.js
import str from "./file0";
console.log(str);

//file0.js
import str1 from "./file1";
console.log(str1)
console.log("file0.js runs");
export default "str0";

//file1.js
import str0 from "./file0";
console.log(str0)
console.log("file1.js runs");
export default "str1";

ES6新特性:使用export和import实现模块化详解

如果导出的是对象:

那么第一行会先输出一个初始值{},在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的对象;

如果是数组:

那么第一行会输出一个被静态分析过的初始值undefined,在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的对象;

如果是布尔值:

那么第一行会输出一个被静态分析过的初始值undefined,在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的布尔值;

为什么会这样呢? 我好像在这边找到了答案:http://exploringjs.com/es6/ch_modules.html#_modules ,ES6的import和export被提前到js的最顶层, 在函数或者对象,或者基本值被导出去的时候提前被静态分析过,参考:http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule , http://www.ecma-international.org/ecma-262/6.0/#sec-toplevelmoduleevaluationjob

结论:用ES6的export导出数据接口的时候, 最好统一用函数, 避免在循环依赖的时候, 因为JS会把不同类型的对象静态解析成不同的初始值;

浏览器兼容:

  1. chrome浏览器目前不支持import,和export;
  2. 火狐的支持也有限, 比chrome好;
  3. 我都用babel;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Mootools 1.2教程 Tooltips
Sep 15 Javascript
js创建数据共享接口——简化框架之间相互传值
Oct 23 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
Jul 31 Javascript
js如何取消事件冒泡
Sep 23 Javascript
JS获取select的value和text值的简单实例
Feb 26 Javascript
AngularJs 弹出模态框(model)
Apr 07 Javascript
简单了解JavaScript操作XPath的一些基本方法
Jun 03 Javascript
Summernote实现图片上传功能的简单方法
Jul 11 Javascript
深入浅析JavaScript中的3DES
Aug 24 Javascript
vue之数据交互实例代码
Jun 16 Javascript
layui弹出层按钮提交iframe表单的方法
Aug 20 Javascript
详解Vue2 添加对scss的支持
Jan 02 Javascript
node koa2实现上传图片并且同步上传到七牛云存储
Jul 31 #Javascript
Angular.js初始化之ng-app的自动绑定与手动绑定详解
Jul 31 #Javascript
详解React中的组件通信问题
Jul 31 #Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
Jul 31 #Javascript
Angular.js中数组操作的方法教程
Jul 31 #Javascript
BootStrap导航栏问题记录
Jul 31 #Javascript
Angular4 中内置指令的基本用法
Jul 31 #Javascript
You might like
WML,Apache,和 PHP 的介绍
2006/10/09 PHP
PHP-Java-Bridge使用笔记
2014/09/22 PHP
浅谈PHP中关于foreach使用引用变量的坑
2016/11/14 PHP
ThinkPHP3.1.x修改成功与失败跳转页面的方法
2017/09/29 PHP
关于javascript中的typeof和instanceof介绍
2012/12/04 Javascript
jQuery不兼容input的change事件问题解决过程
2014/12/05 Javascript
jQuery检测输入的字符串包含的中英文的数量
2015/04/17 Javascript
通过实例理解javascript中没有函数重载的概念
2015/06/03 Javascript
JS实现点击上移下移LI行数据的方法
2015/08/05 Javascript
学习javascript面向对象 理解javascript对象
2016/01/04 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
2017/07/05 Javascript
echart简介_动力节点Java学院整理
2017/08/11 Javascript
js es6系列教程 - 新的类语法实战选项卡(详解)
2017/09/02 Javascript
微信小程序实现tab切换效果
2017/11/21 Javascript
JS复杂判断的更优雅写法代码详解
2018/11/07 Javascript
JavaScript实现电灯开关小案例
2020/03/30 Javascript
JavaScript Tab菜单实现过程解析
2020/05/13 Javascript
vue页面引入three.js实现3d动画场景操作
2020/08/10 Javascript
[01:05:29]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Aster BO3 第二场 1月24日
2021/03/11 DOTA
[04:59]DOTA2-DPC中国联赛 正赛 Ehome vs iG 选手采访
2021/03/11 DOTA
wxpython 最小化到托盘与欢迎图片的实现方法
2014/06/09 Python
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
对Python中type打开文件的方式介绍
2018/04/28 Python
对pandas中时间窗函数rolling的使用详解
2018/11/28 Python
python调用c++传递数组的实例
2019/02/13 Python
简单了解python中的与或非运算
2019/09/18 Python
Tensorflow读取并输出已保存模型的权重数值方式
2020/01/04 Python
Nisbets爱尔兰:英国最大的厨房和餐饮设备供应商
2019/01/26 全球购物
Arti-shopping中文官网:大型海外商品一站式直邮平台
2020/03/23 全球购物
施惠特软件测试面试题以及笔试题
2015/05/13 面试题
护理专业应届毕业生推荐信
2013/11/15 职场文书
新学期红领巾广播稿
2014/01/14 职场文书
应届毕业生如何写求职信
2014/02/16 职场文书
国际贸易专业求职信
2014/06/04 职场文书
计算机实训报告范文
2014/11/05 职场文书
Python turtle编写简单的球类小游戏
2022/03/31 Python