javascript一直没有模块的概念,不过2015年ES6的出现,解决了这一问题。ES6模块的设计思想是尽量静态化,使得编译时就能确定模块间的依赖关系,以及输入输出的变量。
ES6模块功能主要由export和import两个命令构成。export命令用于规定模块的输出接口,import用于载入其他模块的功能。一个模块就是一个js文件。
1.输出单个变量或函数。
export var m = 1;
export var name = "wlk";
export function func(){};
2.输出多个变量或函数。
var m =1,n=2;
export {m,n};
function wlk(){};
function hq(){};
export {wlk,hq};
1.export语句输出的接口与其对应的值是动态绑定关系,即通过该接口获取到的是模块内部实时的值。
例如:
export var foo = "bar";
setTimeout(()=>foo="baz",500);
上面代码输出的接口(变量)是foo,值为bar,但是500ms之后变成baz,引入该模块的模块中的相应的值也会改变。
2.export命令可以出现在模块的任何位置,只要处于模块的顶层作用域就可以。
下面代码就会报错,因为export命名处在了函数作用域。
function foo(){
export var wlk = "bar";
}
import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名必须与被导入模块对外接口名称相同。不过可以用as关键字对大括号里面变量进行改名。
比如:
import {firstname as name} from "test.js";//as就将firstname改名为name
1.用ES6模块import的变量是只读的,不能进行修改。
比如:
//lib.js
export let obj = {name:"wlk"};
//main.js
import {obj} from "lib.js";
obj.age = 22;//可以
obj.name = "hq";//不可以
上面代码中,main.js从lib.js输入对象obj,可以对obj添加属性,但是不能重新赋值。
2.import命令具有提升效果,会提升到整个模块的头部并首先执行。
比如:
foo();
import {foo} from "test.js";
上面代码不会出错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码执行之前。
3.import是静态执行,所以不能使用表达式和变量等只有在运行时才能得到结果的语法结构。
比如:
import {'f'+'oo'} from "test.js"//报错。
let wlk = "test.js";
import {foo} from wlk;//报错。
if(x===1){
import {foo} from "test.js";
}//报错
上面三种写法都会报错,因为他们用到了表达式、变量和if结构,这些是只有运行时才可得到结果的。
4.如果多次执行同一import语句,那么只会执行一次,而不会执行多次。
比如:
import {foo} from "test.js";
import {foo} from "test.js";
其实只相当于执行语句一次。
除了加载某个/些输出值,模块也可以整体加载,即用星号*来指定一个对象,所有输出值都在这个对象上。
如:
//circle.js
export function area(r){
return Math.PI*r*r;
};
export function circum(r){
return 2*Matn.PI*r;
}加载这个模块。
//main.js
import {area,circum} from "circle.js";//普通加载某个/些输出
//或者
import * as circle from "circle.js";//整体加载,并保存到circle这个变量中
注意,模块整体加载所在的对象应该是可以静态分析的,所以不允许运行时修改。
也就是下面的做法是不允许的。
import * as obj from "circle.js";
//这两行是不可以的
obj.foo = "hello";//添加属性
obj.area = function(){};//修改属性
在上面的所有例子中,不难发现,使用import命令时用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户希望快速上手,未必愿意阅读文档去了解模块有哪些属性和方法。为了方便用户,使其不用阅读文档就能加载模块,可以使用export default 命令来为模块指定默认输出。默认输出也像正常输出一样,可以输出变量、函数、对象等,但要注意一点,export default只能在一个模块中使用一次。 export default在本质上就是输出一个叫default的变量,该变量保存了我们要输出的函数,对象或变量等,我们可以在需要输入的模块中给default取任意名字。
给几个使用的例子:
export default function(){};
function foo(){};
export default foo;
var m = 1;
export default m;//注意与正常输出的区别:正常输出应该是,export var m = 1;
export default {
name:"wlk",
sing:function(){}
}
在其他模块中导入时写法都一样:
import 任意名 from "test.js";
此时"任意名"这个变量里保存的就是对应的变量、函数或者对象。
export与import符合写法:
如果在一个模块中,先导入了一个模块,然后又要将其导出,则可以使用复合写法。
export {foo,bar} from "test.js";//这就是复合写法
//等同于
import {foo,bar} from "test.js";
export {foo,bar};
模块也有一个继承的概念,其实它也就是一个模块import另一个模块,然后在统一export而已。
例:
假设circleplus模块继承circle模块。
//circleplus.js
export {foo,bar} from "circle.js";//这里是复合写法
export function fun(){}
//现在circleplus就继承了circle模块,因为circleplus导出的内容中有circle的内容,又添加了自己的内容。
ES6模块的语法其实也就这些,关键是多用。