export和import实现模块化

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

  现在ES6自带了模块化, 也是JS第一次支持module, 在很久以后 ,我们可以直接作用importexport在浏览器中导入和导出各个模块了, 一个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

    

  下面列出几种importexport的基本语法:

  第一种导出的方式:

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

运行下面代码

//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模块 与AMDCMD的区别之一,以下代码修改自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")}

  

  如果导出的是字符串:

运行下面代码

//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.jsfile1.js执行完毕以后, 才输出file0.js导出的对象;

  如果是数组:

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

  如果是布尔值:

  那么第一行会输出一个被静态分析过的初始值undefined,在最后等待file0.jsfile1.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

  

export和import实现模块化的更多相关文章

  1. ES6新特性:使用export和import实现模块化

    在ES6前, 前端就使用RequireJS或者seaJS实现模块化, requireJS是基于AMD规范的模块化库,  而像seaJS是基于CMD规范的模块化库,  两者都是为了为了推广前端模块化的工 ...

  2. ES6新特性:使用export和import实现模块化(转载)

    在ES6前, 前端就使用RequireJS或者seaJS实现模块化, requireJS是基于AMD规范的模块化库,  而像seaJS是基于CMD规范的模块化库,  两者都是为了为了推广前端模块化的工 ...

  3. 让node支持es模块化(export、import)的方法

    node版本v7.9.0,支持了大部分es6的功能,但还不支持es6模块化(export.import). 检测ES6 可以使用es-checker来检测当前Node.js对ES6的支持情况. 使用命 ...

  4. ES6 模块化(Module)export和import详解 export default

    ES6 模块化(Module)export和import详解 - CSDN博客 https://blog.csdn.net/pcaxb/article/details/53670097 微信小程序笔记 ...

  5. module.exports,exports,export和export default,import与require区别与联系【原创】

    还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...

  6. module.exports,exports,export和export default,import与require区别与联系

    还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...

  7. 【从翻译mos文章】不再用par file如果是,export or import 包含大写和小写表名称表

    不再用par file如果是,export or import 包含大写和小写表名称表 参考原始: How to Export or Import Case Sensitive Tables With ...

  8. ES6中的export以及import的使用多样性

    模块功能主要由两个命令构成:export和import.export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能. 一.export导出模块使用部分的几种方式 一个模块就是一 ...

  9. ES6模块之export和import详解

    ES6中的模块即使一个包含JS代码的文件,在这个模块中所有的变量都是对其他模块不可见的,除非我们导出它.ES6的模块系统大致分为导出(export)和导入(import)两个模块. 1.模块导出(ex ...

随机推荐

  1. WPF自学笔记

    WPF使用哪几种元素作为顶级元素: 1. Window元素 2. Page元素(与Window元素类似,用于可导航的应用程序) 3. Application元素(定义应用程序资源和启动设置) PS:在 ...

  2. java concurrency: ThreadLocal及其实现机制

    转载:http://shmilyaw-hotmail-com.iteye.com/blog/1703382 ThreadLocal概念 从字面上来理解ThreadLocal,感觉就是相当于线程本地的. ...

  3. Hibernate之工具类HibernateUtil

    原创文章,转载请注明:Hibernate之工具类HibernateUtil  By Lucio.Yang 1.最简单的工具类,实现SessionFactory的单例共享,session的管理 pack ...

  4. Oracle Hint用法总结

    1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化. 例如: SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_I ...

  5. OCP-1Z0-053-V13.02-712新题

       Why does the number of blocks for the table remain the sale after the shrink operation? A.Because ...

  6. Uva 10935 Throwing cards away I

    题目意思:有N张牌,标号为1~N,且牌以叠好,从上到小就是标号1-N的牌,只要牌堆数量大于等于2的时候,就采取如下操作:将最上面的牌扔掉(即离开牌堆).刚才那张牌离开后,再将新的最上面的牌放置于牌堆最 ...

  7. BZOJ 1699: [Usaco2007 Jan]Balanced Lineup排队

    1699: [Usaco2007 Jan]Balanced Lineup排队 Description 每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. ...

  8. as3声谱效果,有在线演示地址,能够播放本地音乐

    来源:潮汕IT男 简单的as3声谱效果,能够播放本地音乐. tag=as3" style="word-wrap:break-word; margin:0px; padding:0p ...

  9. IF的使用

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. C - Virtual Friends

    网上搜了,好多c++里的东西啊 有思路不会做,真烦,还是好好学c++: 先把题和代码粘过来,过几天学c++好了再看 http://acm.hust.edu.cn/vjudge/contest/view ...