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 相关文章推荐
7款风格新颖的jQuery/CSS3菜单导航分享
Apr 23 Javascript
在JavaScript中访问字符串的子串
Jul 07 Javascript
浅谈javascript中的事件冒泡和事件捕获
Dec 28 Javascript
基于jQuery实现手风琴菜单、层级菜单、置顶菜单、无缝滚动效果
Jul 20 jQuery
微信小程序实现跟随菜单效果和循环嵌套加载数据
Nov 21 Javascript
Vue2.5通过json文件读取数据的方法
Feb 27 Javascript
React如何避免重渲染
Apr 10 Javascript
vue mounted组件的使用
Jun 18 Javascript
解决vue-quill-editor上传内容由于图片是base64的导致字符太长的问题
Aug 20 Javascript
Vue组件间数据传递的方式(3种)
Jul 13 Javascript
vue 授权获取微信openId操作
Nov 13 Javascript
JS 基本概念详细介绍
Oct 16 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
PHP的FTP学习(二)
2006/10/09 PHP
php+javascript的日历控件
2009/11/19 PHP
CI框架教程之优化验证码机制详解【验证码辅助函数】
2019/04/16 PHP
Laravel框架下载,安装及路由操作图文详解
2019/12/04 PHP
JAVASCRIPT HashTable
2007/01/22 Javascript
来自国外的14个图片放大编辑的jQuery插件整理
2010/10/20 Javascript
jquery插件lazyload.js延迟加载图片的使用方法
2014/02/19 Javascript
Javascript学习笔记之 对象篇(三) : hasOwnProperty
2014/06/24 Javascript
jQuery中的jQuery()方法用法分析
2014/12/27 Javascript
jQuery网页版打砖块小游戏源码分享
2015/08/20 Javascript
详解nodejs爬虫程序解决gbk等中文编码问题
2017/04/06 NodeJs
AngularJS中使用three.js的实例详解
2017/07/21 Javascript
javaScript中的空值和假值
2017/12/18 Javascript
vue.js element-ui validate中代码不执行问题解决方法
2017/12/18 Javascript
详解webpack打包第三方类库的正确姿势
2018/10/20 Javascript
Sublime Text3 配置 NodeJs 环境的方法
2020/05/20 NodeJs
小程序实现录音功能
2020/09/22 Javascript
Python对象的深拷贝和浅拷贝详解
2014/08/25 Python
python实现文件路径和url相互转换的方法
2015/07/06 Python
如何处理Python3.4 使用pymssql 乱码问题
2016/01/08 Python
python函数的5种参数详解
2017/02/24 Python
Python(Django)项目与Apache的管理交互的方法
2018/05/16 Python
python实现决策树ID3算法的示例代码
2018/05/30 Python
Python SELENIUM上传文件或图片实现过程
2019/10/28 Python
使用pygame写一个古诗词填空通关游戏
2019/12/03 Python
python 日志 logging模块详细解析
2020/03/31 Python
python filecmp.dircmp实现递归比对两个目录的方法
2020/05/22 Python
Python timeit模块原理及使用方法
2020/10/10 Python
ESDlife健康生活易:身体检查预订、搜寻及比较
2019/05/10 全球购物
服务员自我评价
2014/01/25 职场文书
迎元旦广播稿
2014/02/22 职场文书
2014年初级职称工作总结
2014/12/08 职场文书
行政助理岗位职责范本
2015/04/11 职场文书
甲午风云观后感
2015/06/02 职场文书
iPhone13将有八大升级
2021/04/15 数码科技
Python函数对象与闭包函数
2022/04/13 Python