带你重学ES6 | Export(谨记输出的都是变量)

4,561 阅读8分钟

在原始的 JS 时代,是没有模块化的概念的,随着前端项目的越来越大,并且前端的地位越来越主要,急需模块化的概念引入进来。在 ES6 之前,社区制定了一些模块化的方案,如:CommonJS 和 AMD。ES6 之后这两个正在慢慢的被 import 和 export 所取代。

这让我想起了之前面试的时候一个面试官问我 ES6 为什么要引入 import 和 export,我的回答是为了模块化的加载,避免全局污染,然后他问了一个让我至今难忘的问题,为什么是 import 和 export 这两个单词????😓😓😓 当时的我顿时语塞,只能弱弱的回答一句,JS 规范的....

1、export

export 用于规定模块对外的接口,不管你是否定义,export 导出的模块都是处于严格模式,不能用在嵌入式脚本中。

export 导出的语法有两种:

  1. 命名导出(每个模块包含任意数量)
  2. 默认导出(每个模块只包含一个)

1.1、命名导出

如在 a.js 文件中,想要导出几个变量,我们可以写成如下两种形式。

// a.js
// 第一种
export var a = 1;
export var b = 2;
export var c = 3;
// 第二种
var a = 1;
var b = 2;
var c = 3;
export { a, b, c };

以上两种形式是等价的,但是倾向于第二种写法,因为第二种写法便于阅读,能一眼看出要导出的变量是什么。

除了能导出变量,我们在开发中应用最多的就是导出函数和类。

export function foo(a, b) {
  return a + b;
}
// 等价于
function foo(a, b) {
  return a + b;
}
export { foo };
// 应用到箭头函数
const foo = (a, b) => a + b;
export { foo };

导出的名称除了可以是该变量原本的名称外,还可以通过 as 关键词对其重命名。

var a = 1;
export { a as ba as c };

上述通过 as 关键词对 a 变量进行了重命名,并且同一个变量可以重命名多次。

有一点值得注意的是,export 输出的一定是一个变量,变量,变量,重要的事情说三遍。

// 第一种
export 1// 报错
// 第二种
var a = 1;
export a; // 报错

上述两种导出接口的方式均报错,因为输出均不是变量,第二种虽然 a 是变量,但是实际上跟第一种是一样的,只是变换了一下写法,函数和类也是如此。

1.2、默认导出

我们可以通过 export default 命名对模块进行默认导出,并且一个模块中只能有一个默认导出。

export default function (a, b) {
  return a + b;
}

export default function foo(a, b) {
  return a + b;
}

export default 1;

var a = 1;
export default a;

上一节我们说 export 最重要的是导出的接口是变量,在默认导出中发现如果导出的不是变量的话也是可以的,这是因为 default 其实就是一个变量,所以可以导出的不是一个变量,但是 default 后面跟着的是一个变量话,会报错。

export default var a = 1// 报错

2、import

import 命令用于输入其他模块提供的功能。export 有两种语法形式,对应的 import 也有两种语法形式

2.1、当输出是用的 export 语法时

// a.js
var a = 1;
var b = 2;
export { ab };

import { ab } from "a.js";

当 export 输出模块接口时,import 要用大括号来进行导入,默认情况下导入的名称要和输出的名称一致。如果想要更改导入的名称,我们同样可以用 as 关键词进行重命名。

// a.js
var a = 1;
var b = 2;
export { ab };

import { a as a1, b as b1 } from "a.js";

2.2、当输出是用的 export default 语法时

// a.js
var a = 1;
export default a;

import a from "a.js";

也可以和上一节一块使用:

// a.js
var a = 1;
var b = 2;
var c = 3;
export default c;
export { ab };

import c, { ab } from "a.js";

2.3、import 的其他特性

import 表达式引入进来的所有模块都是只读形式,也就是说,不允许在加载模块的脚本里面,改写模块。

import { a } from "a.js";

a = {}; // Syntax Error : 'a' is read-only;

但是如果引入的模块是一个对象类型的话,它的属性是可以更改。

import { a } from "a.js";
a.name = "Jack"; // 合法

虽说此功能合法,但是在实际开发中还是要慎用。

import 后面的 from 指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js 后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。

import { myMethod } from "util";

上面代码中,util 是模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块。

import 还有一个特性时,import 具有提升,会提升到整个文件的最顶端,首先执行。

foo(); // 不会报错
import { foo } from "a.js";

上述例子不会报错的原因就是因为 import 具有提升,提升到了 foo()之前,首先执行。

除此之外,import 不能当做表达式和变量,不能参与运算。

import {'1' + foo} from 'a.js' // 报错
// 报错
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

import 还可以仅仅导入模块,但不做任何的输入。

import "a.js";

这将运行模块中的全局代码, 但实际上不导入任何值。

与上述相反的是,将模块全部导入,整体加载,并且同样支持通过 as 关键词重命名。

import * from 'a.js'

import * as b from 'a.js'

2.4、export 和 import 复合写法

如果在一个模块中先输入后输出同一个模块,那可以运用 export 和 import 复合写法,即 export...from...

export { foo, bar } from "a.js";
// 可以简单的理解为
import { foo, bar } from "a.js";
export { foo, bar };

值得注意的是,上述的 foo 和 bar 并没有引入到当前模块,当前模块相当于一个中转站,只是对 foo 和 bar 进行转发。

同样我们依然可以对转发的模块进行重命名,而且可以通过*对其全部转发。

export { foo as foo1 } from "a.js";
export * from "a.js";

在 ES2020 中还可以对*通过 as 关键词进行重命名。

export * as a from "a.js";
// 等价于
import * as a from "a.js";
export { a };

除此之外,在 export 和 import 复合写法中,我们可以对默认模块进行转发。

export { default } from "a.js";

是不是感觉很奇怪,没错,我第一次见到的时候也很奇怪,但是后期想了一下,export...from...导入变量,default 也是变量的一种,所以这种也就不奇怪了。

依旧可以通过 as 进行重命名。

export { default as foo } from "a.js";

并且还可以将具名接口改为默认接口。

export { foo as default } from "a.js";
// 等价于
import { foo } from "a.js";
export default foo;

2.5、import()

ES2020 中新增了 import(),主要是为了解决无法动态导入的问题,因为之前也说过,JS 为了静态分析优化,所以 import 必须放在整个模块的顶部,并且不能和表达式混合使用,这在有些场景,尤其是 Node 中很是麻烦,这也是为什么 Node 还是偏向用 require 导入接口的原因之一。

使用 import()动态加载接口的用法很简单,括号内填写要动态导入接口就可以。

import("a.js");

import()会返回一个 Promise 对象。

import("a.js").then((value) => console.log(value));

import()的应用,最直观的就是应用在动态加载组件上,我们可以看到 dva 中dva/dynamic其内部主要就是运用了 import(),对组件和路由进行动态加载,达到优化目的。

import dynamic from "dva/dynamic";

const UserPageComponent = dynamic({
  app,
  models: () => [import("./models/users")],
  component: () => import("./routes/UserPage"),
});

此次也是对export和import有了个重新的认识,也希望对你有所帮助。 点个赞吧!💥🧡💖

后语

觉得还可以的,麻烦走的时候能给点个赞,大家一起学习和探讨!

还可以关注我的博客希望能给我的github上点个Start,小伙伴们一定会发现一个问题,我的所有用户名几乎都与番茄有关,因为我真的很喜欢吃番茄❤️!!!

想跟车不迷路的小伙还希望可以关注公众号 前端老番茄 或者扫一扫下面的二维码👇👇👇。

我是一个编程界的小学生,您的鼓励是我不断前进的动力,😄希望能一起加油前进。

本文使用 mdnice 排版