天天看点

ES6中export、export default、import的理解

前言

ES6标准发布后,module成为标准,标准使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们依然采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用​

​export​

​关键字输出该变量。

export导出模块

export语法声明用于导出函数、对象、指定文件(或模块)的原始值。

// 导出单个特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}

// 导出列表
export { name1, name2, …, nameN };

// 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };

// 默认导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// Aggregating modules
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;      

export有两种模块导出方式:命名式导出(名称导出)和默认导出(定义式导出),命名式导出每个模块可以多个,而默认导出每个模块仅一个。

命名式导出

模块可以通过export前缀关键词声明导出对象,导出对象可以是多个。这些导出对象用名称进行区分,称之为命名式导出。

//------ lib.js ------  
export const sqrt = Math.sqrt;  
export function square(x) {  
    return x * x;  
}  
export function add (x, y) {  
    return x + y;  
}  
  
//------ main.js ------  
import { square, add } from 'lib';  
console.log(square(10)); //100  
console.log(add(2,4));  //6        

这样导入的变量名必须和导出的名称一致。以上对于每一个要导出的变量都加了export,我们也可以直接导出一个列表,例如上面的lib.js可以改写成:

//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
    return x * x;
}
function add (x, y) {
    return x + y;
}
export {sqrt, square, add}      

​export​

​列表可以在模块文件最外层作用域的每一处声明,不一定非要把它放在模块文件的末尾。

也可以直接导入整个模块,此时的main.js模块将变成这样。

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(10)); //100
console.log(lib.add(2,4));  //6      

模块导出时,可以指定模块的导出成员。导出成员可以认为是类中的公有对象,而非导出成员可以认为是类中的私有对象:

var name = 'IT笔录';
var domain = 'http://itbilu.com';
  
export {name, domain}; // 相当于导出
{name:name,domain:domain}      

模块导出时,我们可以使用as关键字对导出成员进行重命名:

var name = 'IT笔录';
var domain = 'http://itbilu.com';
  
export {name as siteName, domain};      

注意:export 命令规定的是对外接口,必须与模块内部变量建立一一对应的关系

// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};


// 报错
export 1;

// 报错
var m = 1;
export m;      

报错的写法原因是:没有提供对外的接口,第一种直接输出1,第二种虽然有变量m,但还是直接输出1,导致无法解构。

同样的,​

​function​

​​和​

​class​

​的输出,也必须遵守这样的写法。

// 报错
function f() {}
export f;

// 正确
export function f() {};

// 正确
function f() {}
export {f};      

默认导出

默认导出也被称做定义式导出。命名式导出可以导出多个值,但在在import引用时,也要使用相同的名称来引用相应的值。而默认导出每个导出只有一个单一值,这个输出可以是一个函数、类或其它类型的值,这样在模块import导入时也会很容易引用。

export default function() {}; // 可以导出一个函数
export default class(){}; // 也可以出一个类      

注意这里默认导出不需要用{}。当然也可以使用混合的导出。

//------ lib.js ------
export default function() {...};
export function each() {...};
 
//------ main.js ------
import _,{ each } from 'lib';      

export加default和不加的区别

// 第一组
export default function crc32() { // 输出
  // ...
}
import crc32 from 'crc32'; // 输入
 
 
// 第二组
export function crc32() { // 输出
  // ...
};
import {crc32} from 'crc32'; // 输入      

第一组是使用export default时,对应的import语句不需要使用大括号; 

第二组是不使用export default时,对应的import语句需要使用大括号。 

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字

// 正确
export var a = 1;
 
// 正确
var a = 1;
export default a;
 
// 错误
export default var a = 1;
// 上面代码中,export default a的含义是将变量a的值赋给变量default。所以,最后一种写法会报错。
// 注意,不能使用 var、let 或 const 用于导出默认值 export default。      

因为export default 命令的本质是将后面的值,赋给default变量,所以可以直接将一个值写在export default之后

// 正确
export default 42;
 
// 报错 原因这一句报错是因为没有指定对外的接口,而前一句指定外对接口为default。
export 42;      

如果想在一条import语句中,同时输入默认方法和其他接口,可以写成下面这样

import _, { each, each as forEach } from 'lodash';

===============

export default function (obj) {
  // ···
}
 
export function each(obj, iterator, context) {
  // ···
}
 
export { each as forEach };//暴露出forEach接口,默认指向each接口,即forEach和each指向同一个方法。      

export default也可以用来输出类

// MyClass.js
export default class { ... }
 
// main.js
import MyClass from 'MyClass';
let o = new MyClass();      

命名式导出与默认导出

默认导出可以理解为另一种形式的命名导出,默认导出可以认为是使用了default名称的命名导出。

下面两种导出方式是等价的:

const D = 123;
  
export default D;
export { D as default };      

在导出多个值时,命名导出非常有用。在导入期间,必须使用相应对象的相同名称。但是,可以使用任何名称导入默认导出,例如:

let k; export default k = 12; // 在 test.js文件中定义导出
import m from './test' // 由于k 是默认导出,所以可以自由使用import m 替代import k
console.log(m);        // 输出为 12      

Import加载模块

export定义了模块的对外接口后,其他JS文件就可以通过import来加载这个模块,

// main.js
import {firstName, lastName, year} from './profile';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}      

import命令接受一对大括号,里面指定要从其他模块导入的变量名,必须与被导入模块(​

​profile.js​

​)对外接口的名称相同。

如果想重新给导入的变量一个名字,可以用as关键字,

import { lastName as surname } from './profile';      

import后的from 可以指定需要导入模块的路径名,可以是绝对路径,也可以是相对路径, .js路径可以省略,如果只有模块名,不带有路径,需要有配置文件指定。

注意,​

​import​

​命令具有提升效果,会提升到整个模块的头部,首先执行。(是在编译阶段执行的)

// circle.js。输出两个函数
export function area(radius) {
  return Math.PI * radius * radius;
}
export function circumference(radius) {
  return 2 * Math.PI * radius;
}

// main.js 加载在个模块
import { area, circumference } from './circle';

console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));

//上面写法是逐一指定要加载的方法,整体加载的写法如下。
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));      
import * as circle from './circle';

// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};      
  • ​import​

    ​​和​

    ​export​

    ​命令只能在模块的顶层,不能在代码块之中。否则会语法报错。
  • 这样的设计,可以提高编译器效率,但是没有办法实现运行时加载。
  • 因为require是运行时加载,所以import命令没有办法代替require的动态加载功能。
  • 所以引入了import()函数。完成动态加载。