ES2015: import和export

1、模块化

前端模块化的好处都已经被说烂了,概括为两点:html

  1. 避免全局变量污染;
  2. 有效的处理依赖关系。

ES2015终于引入了模块的概念,最近学习了下,顺便记下笔记。前端

2、准备工做

  1. 在Chrome浏览器环境运行代码;
  2. 新建个目录,目录下包含两个文件:
  • index.html
<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <h1>import</h1>
        <script src="index.js" type="module"></script>
    </body>
</html>
  • index.js

3、模块导出 export

  1. 一个文件定义一个模块,经过export语句导出该模块输出的变量;
  2. export语句有两种语法格式:命名导出默认导出

3.1 命名导出

命名导出就是明确导出的变量名称和值。git

在目录下建立math.js,内容以下:github

// Case 1: export后面跟变量输出声明语句
export var PI = 3.14;

// Case 2: export后面直接跟变量定义语句
export var add = function (x, y) { // 导出函数print
    return x + y;
}

这表示math.js模块导出变量PIadd。用NodeJS的模块格式可表示为:浏览器

var PI = 3.14;
var add = function (x, y) { // 导出函数print
    return x + y;
}

module.exports.PI = PI; 
module.exports.add = add;

index.js内容:模块化

import * as Math from "./math.js"; // import是导入模块,后面会说。

console.log(Math.PI);
console.log(Math.add(1, 2));

用浏览器打开页面,看看输出结果是否OK:函数

3.14
3

若是导出多个变量,能够采用简写格式:

// 调整math.js内容
var PI = 3.14;
var add = function (x, y) { 
    return x + y;
}

export { PI, add }; // 简写格式,统一列出须要输出的变量

重命名导出变量

简写格式还能够对输出的变量重命名:学习

// 再次修改math.js
var PI = 3.14;

var add = function (x, y) { 
    return x + y;
}

export { PI, add as Add}; // 把输出变量add重命名为Add(注意不用双引号)

经过关键字as把输出变量add重命名为Add(Add是个字面量,不是字符串不须要引号)。prototype

一样在index.js模块也要修改下:code

import * as Math from "./math.js";

console.log(Math.PI);
console.log(Math.Add(1, 2)); // Add方法名称改动了。

export语句后面能够跟什么?

命名导出须要同时指定导出的变量名称和变量值,因此export语句后面跟的通常是:

  • 变量(函数)声明语句;
  • export简写格式。

不能够是表达式,由于表达式只有值,没有名字。

// 语法错误:Declaration or statement expected
export 3.14

3.2 默认导出

经过关键字default修饰export能够指定一个模块的默认输出变量值(在导入模块的默认输出时,不须要指定导出变量名称,这个后面再说)。

// Case 3 常量
export default 25; 

// Case 4 变量
var PI = 3.14;
export default PI 

// Case 5 函数
export default function add2( x, y) {
    return x + y;
}
  1. 一个模块最多只能有一个默认导出;
  2. 默认输出能够视为名字是default的模块输出变量;
  3. 默认导出后面能够是表达式,由于它只须要值。
export default 3.14

3.3 总结:

  1. 一个文件一个模块;
  2. export命名导出;
  3. export default默认导出;
  4. export语句必须在模块做用域的最顶层,即export不能够出如今任意花括号内,如函数语句里,子代码块里(控制语句)。

4、模块导入

经过import语句导入外部模块。对应export语句的两种导出方式,import也分别存在两种不一样的模块导入语法格式。

4.1 导入模块的命名输出

修改index.js:

import { PI, Add } from './math.js';
console.log(PI);
console.log(Add(1, 2));

表示:导入math.js模块里输出的变量PI, Add
注意名称必需要和math.js模块的输出变量一一对应,不然就是undefined

重命名导入的变量

该格式还支持对导入的变量重命名:

import { PI as pi, Add as add} from './math.js';

通配符*

若是导入一个模块全部命名输出,可采用通配符*

// 修改index.js
import * as Math from './math.js'; // 此时必须经过as指定个别名
console.log(Math.PI);
console.log(Math.Add(1, 2));

表示导入模块math.js全部命名输出变量,并经过Math变量访问全部命名导出的变量。
Math变量是个特殊的对象,叫模块对象

Object.prototype.toString.call(Math); // [object Module]

而且这个对象和它的属性都是只读的(后面细说)。

4.2 导入模块的默认输出

// 修改math.js:
var PI = 3.14;

var add = function (x, y) { 
    return x + y;
}

export { PI, add as Add }; // 简写格式,统一列出须要输出的变量

export default function say() { // 默认输出
    console.log("I am default export");
}

修改index.js:

import say  from "./math.js";
say();
  1. 表示导入模块math.js的默认输出,此时能够用as重命名;
    能够利用重命名方式避免导入模块的变量名称和本模块变量命名冲突。
  2. 导入模块默认输出的名字能够任意命名。

    import say2  from "./math.js"; // 
    say2();
  3. 若是同时导入模块的命名输出和默认输出,可采用格式:
import say, * as Math from './math.js'; 
// OR
import say, { PI, Add } from './math.js';

默认导入必定放在命名导入前面

// 非法
import * as Math, say from './math.js'; 

// 非法
import { PI, Add }, say from './math.js';

4.3 只导入

若是只导入一个模块,但不引用模块的输出变量,能够简写为:

import './math.js'

此时只会触发模块math.js的执行。

4.4 总结:

  1. import xx from "xxx"导入默认输出;
  2. import { xx } from "xxx"导入指定的命名输出;
  3. import * as xx from "xxx"导入所有命名输出;
  4. import "xxx"只导入;
  5. 一样import语句也必须在模块的最顶层做用域里,即import不能够出如今任意花括号内,如函数语句里,子代码块里(控制语句)。
    注意在动态导入没这个限制
  6. 模块都是在严格模式下执行的。

5、修改导入/导出的变量值

5.1 修改导入的变量值

模块能够导出任何类型变量(引用变量和值变量),若是在模块index.js里修改了模块math.js导出的引用变量或者值变量,那会影响模块math.js里的值么?
很遗憾,import导入的变量和变量的属性都是只读的,不能也不该该修改引入的变量值。

import * as Math from './math.js'; 
// TypeError: Cannot assign to read only property 'Count' of object '[object Module]'
Math.Count = 12;

5.2 修改导出的变量值

反过来想一想,若是模块math.js修改了其导出的引用变量或者值变量在,那会影响模块index.js里的取值么?
修改math.js:

var Count = 0;
var increase =  function() {
    Count++;
}
var Person = {
    name: 'Bob'
}
var changeName = function() {
    Person.name = 'John';
}

export { Count, Person, increase, changeName };

修改index.js:

import * as Math from './math.js'; 

console.log(`Person:${JSON.stringify(Math.Person)}, Count:${Math.Count}`);// 修改前
Math.increase();
Math.changeName();
console.log(`Person:${JSON.stringify(Math.Person)}, Count:${Math.Count}`);//  修改后

输出:

Person:{"name":"Bob"}, Count:0
Person:{"name":"John"}, Count:1

从输出能够看出只要math.js修改了导出值就会影响index.js的取值。

5.3 小结

摘自MDN import

The static import statement is used to import read only live bindings which are exported by another module
  1. read only:引用方修改导入模块的变量值不影响原模块的变量值;
  2. live bindings:模块修改了其输出变量的值会影响其余引入模块的取值。

6、再看export语句

了解了exportimport基本用法后,咱们再看看export语句另外一个语法规则:导出引入的模块的变量。
上面的例子里export语句都是导出模块自身定义的变量,其实export还能够导出模块引入模块的输出。
在目录添加文件log.js:

export var Log = function(msg) {
    console.log(msg);
}
export default 'I am log.js';

修改math.js:

var Count = 0;
var increase =  function() {
    Count++;
}
var Person = {
    name: 'Bob'
}

var changeName = function() {
    Person.name = 'John';
}

export { Count, Person, increase, changeName}; 
export default function say() {
    console.log(`Person:${JSON.stringify(Person)}, Count:${Count}`)
}
export * from './log.js'; //

修改index.js:

import say, * as Math from './math.js'; 

Math.Log('hello'); // 该方法来之log.js模块

查看下输出是否OK。

其中export * from './log.js';表示导出模块log.js全部的命名输出。等价于:

export { Log } from './log.js';

注意: 这种语法格式export * from './log.js';不能够定义别名,而花括号的语法格式export { Log } from './log.js'能够定义别名,即:

export { Log as log} from './log.js'

怎么在math.js模块里导出模块log.js的默认输出呢?
只能采用先导入,再导出方式:

import logName from './log.js';
export { logName }

参考

整理自gitHub笔记ES2015模块 & import, export

相关文章
相关标签/搜索