ES6新特性:使用export和import实现模块化
在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()); //
console.log(diag(, )); //

下面列出几种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();

第二种导出的方式:
在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);

第三种导出的方式:
这种方式是直接在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);

第四种导出的方式:
这种导出的方式不需要知道变量的名字, 相当于是匿名的, 直接把开发的接口给export;
如果一个js模块文件就只有一个功能, 那么就可以使用export default导出;
//lib.js
export default "string"; //main.js
import defaultString from "./lib";
console.log(defaultString);

第五种导出方式:
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的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 = ;
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); //
incCounter();
console.log(counter); //
setCounter();
console.log(counter); //
在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")}

如果导出的是字符串:
//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";

如果导出的是对象:
那么第一行会先输出一个初始值{},在最后等待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
1
浏览器兼容:
chrome浏览器目前不支持import,和export;
火狐的支持也有限, 比chrome好;
我都用babel;
参考:
ecma-262:http://www.ecma-international.org/ecma-262/6.0/#sec-imports
import:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
export:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
babelJS:http://babeljs.io/
Exploring ES6(Dr.Axel Rauschmayer):http://exploringjs.com/es6/ch_modules.html
SeaJS和RequireJS的异同:https://github.com/seajs/seajs/issues/277
SeaJS与RequireJS最大的区别:https://www.douban.com/note/283566440/
阮老师es6#module:http://es6.ruanyifeng.com/#docs/module
AMD 和 CMD 的区别有哪些:https://www.zhihu.com/question/20351507/answer/14859415
作者: NONO
出处:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830
ES6新特性:使用export和import实现模块化的更多相关文章
- ES6新特性概览
本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...
- 必须掌握的ES6新特性
ES6(ECMAScript2015)的出现,让前端开发者收到一份惊喜,它简洁的新语法.强大的新特性,带给我们更便捷和顺畅的编码体验,赞! 以下是ES6排名前十的最佳特性列表(排名不分先后): 1.D ...
- 你不知道的JavaScript--Item24 ES6新特性概览
ES6新特性概览 本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代 ...
- 前端入门21-JavaScript的ES6新特性
声明 本篇内容全部摘自阮一峰的:ECMAScript 6 入门 阮一峰的这本书,我个人觉得写得挺好的,不管是描述方面,还是例子,都讲得挺通俗易懂,每个新特性基本都还会跟 ES5 旧标准做比较,说明为什 ...
- 34.js----JS 开发者必须知道的十个 ES6 新特性
JS 开发者必须知道的十个 ES6 新特性 这是为忙碌的开发者准备的ES6中最棒的十个特性(无特定顺序): 默认参数 模版表达式 多行字符串 拆包表达式 改进的对象表达式 箭头函数 =&> ...
- ES6新特性概览1
本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...
- ES6新特性之模板字符串
ES6新特性概览 http://www.cnblogs.com/Wayou/p/es6_new_features.html 深入浅出ES6(四):模板字符串 http://www.infoq.c ...
- Atitit js版本es5 es6新特性
Atitit js版本es5 es6新特性 Es5( es5 其实就是adobe action script的标准化)1 es6新特性1 Es5( es5 其实就是adobe action scrip ...
- ES6新特性:Proxy代理器
ES6新特性:Proxy: 要使用的话, 直接在浏览器中执行即可, node和babel目前还没有Proxy的polyfill;,要使用的话,直接在浏览器中运行就好了, 浏览器的兼容性为:chrome ...
- ES6新特性(函数默认参数,箭头函数)
ES6新特性之 函数参数的默认值写法 和 箭头函数. 1.函数参数的默认值 ES5中不能直接为函数的参数指定默认值,只能通过以下的变通方式: 从上面的代码可以看出存在一个问题,当传入的参数为0或者 ...
随机推荐
- Xcode计算缓存文件大小和清除缓存
//获得缓存路径 self.cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, ...
- AutoLayout(自动布局)
1. iOS两种自适应布局方式:(修正说明:) -AutoLayout(自动布局) + SizeClasses(尺寸类别) -Autoresizing (自动调整尺寸/弹簧式调整尺寸) 前者 Auto ...
- Xcode出现( linker command failed with exit code 1)错误总结
这种问题,通常出现在添加第三方库文件或者多人开发时. 这种问题一般是找不到文件而导致的链接错误. 我们可以从如下几个方面着手排查. 先可以再试试一下几个方法: 1,看看是不是有新添加的文件跟之前文件 ...
- Android 热修复技术(1)---原理
热修复技术分为几部分: 原理介绍 Android HotFix源码分析 自定义框架 1.Android分包MultiDex原理 首先Dex是什么东西? Dex就是Window里面的exe文件 也就是可 ...
- ToList()方法
//ToList()方法,翻译:把****转化为List集合. // 控制台试试: string[] fruits = { "apple", "passionfruit& ...
- .NET重构—单元测试的代码重构
阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1.1.公用的MOCK对象: ...
- 在Windows Server 2012 R2中搭建SQL Server 2012故障转移集群
需要说明的是我们搭建的SQL Server故障转移集群(SQL Server Failover Cluster)是可用性集群,而不是负载均衡集群,其目的是为了保证服务的连续性和可用性,而不是为了提高服 ...
- 附加数据库后无法创建发布,error 2812 解决
日前,由于业务需要,我要把一个数据库直接迁移到新的实例上 用的方法比较古老,就是直接停旧服务器,将数据文件复制到新服务器上,附加数据库 当然这个附加没有什么可说的了,但是在附加后需要将原来库上的发布重 ...
- 找不到mysql.sock,mysql.sock丢失问题解决方法
Can 't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock '(2) "; 是你的mysql ...
- Linux服务器磁盘扩展和oracle表空间文件迁移操作记录
1.环境介绍 服务器硬件:Dell R710 服务器OS:红帽子Linux RHEL4.8 数据库:Oracle 10g 2.出现的问题 因为数据表每天有上百万的数据写入表,加上建立索引,导致表空 ...