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 相关文章推荐
利用JS重写Cognos右键菜单的实现代码
Apr 11 Javascript
Jquery知识点三 jquery表单对象操作
Jan 17 Javascript
关于jquery中全局函数each使用介绍
Dec 10 Javascript
javascript基于HTML5 canvas制作画箭头组件
Jun 25 Javascript
实例剖析AngularJS框架中数据的双向绑定运用
Mar 04 Javascript
使用JQuery 加载页面时调用JS的实现方法
May 30 Javascript
基于Javascript倒计时效果
Dec 22 Javascript
ES6中的箭头函数实例详解
Apr 06 Javascript
js学习总结之DOM2兼容处理顺序问题的解决方法
Jul 27 Javascript
在NPM发布自己造的轮子的方法步骤
Mar 09 Javascript
js中数组常用方法总结(推荐)
Apr 09 Javascript
vue项目在线上服务器访问失败原因分析
Aug 14 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
echo(),print(),print_r()之间的区别?
2006/11/19 PHP
php中文字母数字验证码实现代码
2008/04/25 PHP
php木马webshell扫描器代码
2012/01/25 PHP
Codeigniter的dom类用法实例
2015/06/26 PHP
php开发工具有哪五款
2015/11/09 PHP
使用PHP实现微信摇一摇周边红包
2016/01/04 PHP
php 使用fopen函数创建、打开文件详解及实例代码
2016/09/24 PHP
简单谈谈PHP中的Reload操作
2016/12/12 PHP
理解Javascript_01_理解内存分配原理分析
2010/10/11 Javascript
基于jquery实现状态限定编辑的代码
2012/02/11 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
JavaScript中this关键词的使用技巧、工作原理以及注意事项
2014/05/20 Javascript
20条学习javascript的编程规范的建议
2014/11/28 Javascript
Javascript中神奇的this
2016/01/20 Javascript
JS中使用apply方法通过不同数量的参数调用函数的方法
2016/05/31 Javascript
Sequelize中用group by进行分组聚合查询
2016/12/12 Javascript
详解前端自动化工具gulp自动添加版本号
2016/12/20 Javascript
jQury Ajax使用Token验证身份实例代码
2017/09/22 Javascript
深入浅析JS中的严格模式
2018/06/04 Javascript
vue2实现搜索结果中的搜索关键字高亮的代码
2018/08/29 Javascript
纯 JS 实现放大缩小拖拽功能(完整代码)
2019/11/25 Javascript
python使用tkinter库实现五子棋游戏
2019/06/18 Python
Django Form 实时从数据库中获取数据的操作方法
2019/07/25 Python
numpy:np.newaxis 实现将行向量转换成列向量
2019/11/30 Python
python修改linux中文件(文件夹)的权限属性操作
2020/03/05 Python
Django choices下拉列表绑定实例
2020/03/13 Python
Python-for循环的内部机制
2020/06/12 Python
tensorflow下的图片标准化函数per_image_standardization用法
2020/06/30 Python
scrapy在python爬虫中搭建出错的解决方法
2020/11/22 Python
浅析HTML5的WebSocket与服务器推送事件
2016/02/19 HTML / CSS
英文简历中的自荐信范文
2013/12/14 职场文书
ktv收银员岗位职责
2013/12/16 职场文书
《要下雨了》教学反思
2014/02/17 职场文书
通用自荐信范文
2014/03/14 职场文书
超市中秋节促销方案
2014/03/21 职场文书
2014年卫生工作总结
2014/11/27 职场文书