zl程序教程

您现在的位置是:首页 >  工具

当前栏目

每天3分钟,重学ES6-ES12(十八)ES Module

ESES6 分钟 每天 module 十八 重学 ES12
2023-06-13 09:12:50 时间

theme: juejin

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

每天3分钟,重学ES6-ES12文章汇总

前言

今天开始和大家一起系统的学习ES6+,每天3分钟,用一把斗地主的时间,重学ES6+,前面我们介绍了模块化的历史,今天介绍模块化处理方案

ES Module

  • JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等, 所以在ES推出自己的模块化系统时,大家也是兴奋异常。
  • ES Module和CommonJS的模块化有一些不同之处:
    • 一方面它使用了import和export关键字;
    • 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
  • ES Module模块采用export和import关键字来实现模块化:
    • export负责将模块内的内容导出;
    • import负责从其他模块导入内容;
  • 采用ES Module将自动采用严格模式:use strict

exports关键字

  • export关键字将一个模块中的变量、函数、类等导出;
  • 我们希望将其他中内容全部导出,它可以有如下的方式:
    • 方式一:在语句声明的前面直接加上export关键字
    • 方式二:将所有需要导出的标识符,放到export后面的 {}中
      • 注意:这里的 {}里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象的;
      • 所以: export {name: name},是错误的写法;
  • 方式三:导出时给标识符起一个别名 代码演示 ```js // 1.第一种方式: export 声明语句 export const name = "why" export const age = 18

export function foo() { console.log("foo function") }

export class Person { }

// 2.第二种方式: export 导出 和 声明分开 const name = "why" const age = 18 function foo() { console.log("foo function") }

export { name, age, foo }

// 3.第三种方式: 第二种导出时起别名 export { name as fName, age as fAge, foo as fFoo }

## import关键字
* import关键字负责从另外一个模块中导入内容 

* 导入内容的方式也有多种:

    * 方式一:import {标识符列表} from '模块';
        * 注意:这里的{}也不是一个对象,里面只是存放导入的标识符列表内容;

    * 方式二:导入时给标识符起别名
    
    * 方式三:通过 * 将模块功能放到一个模块功能对象(a module object)上
    
### 代码演示
```js
// 1.导入方式一: 普通的导入
import { name, age, foo } from "./foo.js"
import { fName, fAge, fFoo } from './foo.js'

// 2.导入方式二: 起别名
import { name as fName, age as fAge, foo as fFoo } from './foo.js'

// 3.导入方式三: 将导出的所有内容放到一个标识符中
import * as foo from './foo.js'

console.log(foo.name)
console.log(foo.age)
foo.foo()

const name = "main"

console.log(name)
console.log(age)

export和import结合使用

  • 补充:export和import可以结合使用
  • 为什么要这样做呢?
    • 在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
    • 这样方便指定统一的接口规范,也方便阅读;
    • 这个时候,我们就可以使用export和import结合使用;代码演示 ```js import { add, sub } from './math.js' import { timeFormat, priceFormat } from './format.js'

export { add, sub, timeFormat, priceFormat }

## default用法
* 前面我们学习的导出功能都是有名字的导出(named exports): 
    * 在导出export时指定了名字; 
    * 在导入import时需要知道具体的名字;

* 还有一种导出叫做默认导出(default export) 
    * 默认导出export时可以不需要指定名字; 
    * 在导入时不需要使用 {},并且可以自己来指定名字; 
    * 它也方便我们和现有的CommonJS等规范相互操作;

* 注意:在一个模块中,只能有一个默认导出(default export);
### 代码演示
```js
const name = "why"
const age = 18

const foo = "foo value"

// 1.默认导出的方式一:
export {
  // named export
  name,
  // age as default,
  // foo as default
}

// 2.默认导出的方式二: 常见
export default foo

// 注意: 默认导出只能有一个

import函数

  • 通过import加载一个模块,是不可以在其放到逻辑代码中的,比如:
  • 为什么会出现这个情况呢?
    • 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
    • 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况;
    • 甚至下面的这种写法也是错误的:因为我们必须到运行时能确定path的值;
  • 但是某些情况下,我们确确实实希望动态的来加载某一个模块:
    • 如果根据不懂的条件,动态来选择加载模块的路径;
    • 这个时候我们需要使用 import() 函数来动态加载;

import meta

  • import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。
    • 它包含了这个模块的信息,比如说这个模块的URL;
    • 在ES11(ES2020)中新增的特性;代码演示 ```js // import { name, age, foo } from './foo.js'

// console.log(name)

// import函数返回的结果是一个Promise import("./foo.js").then(res => { console.log("res:", res.name) })

console.log("后续的代码都是不会运行的~")

// ES11新增的特性 // meta属性本身也是一个对象: { url: "当前模块所在的路径" } console.log(import.meta)

## ES Module的解析流程
* ES Module的解析过程可以划分为三个阶段:

    * 阶段一:构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(Module Record);

    * 阶段二:实例化(Instantiation),对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向 对应的内存地址。

    * 阶段三:运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中;
 ### 代码演示
```js
// foo.js
let name = "why"
let age = 18

setTimeout(() => {
  name = "kobe"
  age = 40
}, 100)

export {
  name,
  age
}

// main.js
import { name, age } from './foo.js'

console.log(name, age)

setTimeout(() => {
  console.log(name, age)
}, 2000)

// index.html
<html lang="en"><head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  
  <script src="./main.js" type="module"></script>

</body></html>

执行结果为:why 18