【12】ES6:模块化

一、JavaScript 模块化

JavaScript 模块化是一种组织和管理 JavaScript 代码的方法,它将代码分割为独立的模块,每个模块都有自己的作用域,并且可以导出和导入功能。模块化可以提高代码的可维护性、可重用性和可扩展性。

在JavaScript中,有几种常见的模块化规范和工具,包括:

CommonJS:

CommonJS 是一种服务器端 JavaScript 模块化规范,主要用于 Node.js。

使用 require() 函数导入模块,使用 module.exports 或 exports 导出模块。

支持同步加载模块,因此在浏览器端可能会阻塞页面渲染。

适用于服务器端开发,模块的加载是在运行时发生的。
// CommonJS模块
let { stat, exists, readfile } = require('fs')

// 等同于
let _fs = require('fs')
let stat = _fs.stat
let exists = _fs.exists
let readfile = _fs.readfile

上面代码的实质是整体加载 fs 模块(即加载 fs 的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

AMD (Asynchronous Module Definition):

AMD 是一种浏览器端 JavaScript 模块化规范,旨在解决异步加载模块的问题。

使用 define() 函数定义模块,并使用 require() 函数异步导入模块。

支持异步加载模块,在运行时按需加载模块,不会阻塞页面渲染。

适用于浏览器端开发,特别是在网络环境较慢的情况下。

UMD (Universal Module Definition):

UMD 是一种通用的 JavaScript 模块化规范,旨在兼容 CommonJS 和 AMD 规范。

根据环境判断是否支持 CommonJS、AMD 或全局对象,并采取相应的模块化方式。

可以在服务器端和浏览器端都使用,兼容不同的模块化规范。

ES6 (ES2015) 模块化:

ES6 模块化是 ECMAScript 2015 引入的官方标准模块化规范。

使用 import 和 export 关键字来导入和导出模块。

静态分析:可以在编译时进行静态分析,提前处理模块依赖关系。

支持默认导出和命名导出,支持动态导入。

在现代浏览器和Node.js中得到广泛支持。
// ES6模块
import { stat, exists, readFile } from 'fs'

上面代码的实质是从 fs 模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

ES6 模块化是目前被广泛使用的标准模块化规范,它提供了更加简洁、灵活的语法,并且在编译阶段进行静态分析,能够提高性能。大多数现代的 JavaScript 开发都采用 ES6 模块化来组织和管理代码,尤其是在使用构建工具(如Webpack、Rollup等)进行项目构建时。

二、ES6 Module

模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。

1、导出(export)

(1)命名导出(Named Exports): 使用 export 命令指定导出一个变量(var、let、const)、函数(functions)或类(class)。

导出变量

// 导出变量
export const name = 'Amy'
export let age = 20
export const city = 'Beijing'

// 等同于(推荐)
const name = 'Amy'
let age = 20
const city = 'Beijing'
export { name, age, city }
// 使用 as 关键字重命名
export { name as firstName }

// ----------------------------------------
// export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 以下写法报错
export 1 // 直接输出
export name // 通过变量 m,还是直接输出 1

导出函数

export function multiply(x, y) {
	return x * y
}

export function calculateSum(a, b) {
	return a + b
}

// 等同于
function multiply(x, y) {
	return x * y
}
function calculateSum(a, b) {
	return a + b
}
export { multiply, calculateSum }


// 报错
export multiply

导出类

export class Person {
	constructor(name, age) {
		this.name = name
		this.age = age
	}
	
	sayHello() {
		console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
	}
}

(2)默认导出(Default Exports): 使用 export default 命令默认导出一个变量、函数、类。

默认导出变量

// 默认导出一个值
export default 20 


// 导出一个对象作为默认值
const person = {
	name: 'Amy',
	age: 20,
}
export default person

默认导出函数

// 导出一个默认的加法函数
export default function add(a, b) {
	return a + b
}

默认导出类:

export default class Person {
	constructor(name, age) {
		this.name = name
		this.age = age
	}
	
	sayHello() {
		console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
	}
}

2、导入(import)

(1)命名导入(Named Imports): 使用 import { xxx } from url 语法导入模块的指定变量、函数、或类。

export 导出的模块,在导入时不能随意取别名,名称必须与模块导出时相同!并且要使用类似于解构赋值的 {} 形式。

// 命名导出
export const name = 'Amy'
export function sayHello() {
	console.log('Hello!')
}

// ----------------------------------------
// 命名导入
import { name, sayHello } from './module.js'

(2)默认导入(Default Imports): 使用 import xxx from url 语法导入模块的默认导出变量、函数、或类。

export default 导出的模块,在导入时可以取任意名称。并且 import 命令后面,不需要使用大括号。

// 默认导出
const age = 18
export default age

// ---------------------------------------------
// 默认导入
import age from './module.js'

(3)整体导入: 用星号 * 指定一个对象,所有输出值都加载在这个对象上面。

将同一文件里的所有模块导入到一个对象中,不仅对 export 有效,同时对 export default 也同样有效。

// 整体导入
import * as Obj from './module.js'
console.log(Obj) // 见图片
console.log(Obj.fn) // ƒ fn() {}
console.log(Obj.className) // class className {}
console.log(Obj.age) // 18
// export default 也同样有效:imObj.default

在这里插入图片描述

(4)同时导入

当我们需要分别导入 export default 和 export 时,可以使用同时导入的方式。

// 我们可以分开实现
import { fn, className, age } from './module.js'
import sex from './module.js'
// 更推荐使用同时导入的方式
import sex, { fn, className, age } from './module.js'
// 注意:export default 必须在 export 之前

3、导出导入时起别名

export 导出的变量或 import导入的变量,可以使用 as 关键字重命名。

// 导出
function fn() {}
class className {}
const age = 18
export { fn as func, className as cN, age }
// 导入
import { func, cN, age as nl } from './module.js'
console.log(func) // ƒ fn() {}
console.log(cN) // class className {}
console.log(nl) // 18

4、export 与 import 的复合写法

如果在一个模块之中,先输入后输出同一个模块,import 语句可以与 export 语句写在一起。

export { foo, bar } from 'my_module'

// 可以简单理解为
import { foo, bar } from 'my_module'
export { foo, bar }

上面代码中,export和import语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar。

模块的接口改名和整体输出,也可以采用这种写法。

// 接口改名
export { foo as myFoo } from 'my_module'

// 整体输出
export * from 'my_module'

默认接口的写法如下。

export { default } from 'foo'

具名接口改为默认接口的写法如下。

export { es6 as default } from './someModule'

// 等同于
import { es6 } from './someModule'
export default es6

同样地,默认接口也可以改名为具名接口。

export { default as es6 } from './someModule'

ES2020 之前,有一种 import 语句,没有对应的复合写法。

import * as someIdentifier from 'someModule'

ES2020 补上了这个写法。

export * as ns from "mod";

// 等同于
import * as ns from 'mod'
export { ns }

5、export 与 import 的总结

在 ES6 中使用 export、和 export default 向外导出成员; 使用 import 来导入成员。

在一个模块中,可以同时使用 export default 和 export 向外导出成员;export default 只允许向外导出一次。

使用 export default 向外暴露的成员,可以用任意变量接收。

使用 export 向外暴露的成员,必须用 {} 接收,变量之间用逗号分隔,且名称必须与导出时的名称一致。

export 可以向外暴露多个成员,如果某些成员,在 import 导入时,不需要,可以不在 {} 中定义

export 导出的变量或 import导入的变量,可以使用 as 关键字重命名。

6、import() 动态导入

import() 是 ES6 中的动态导入语法,它允许在运行时动态地加载模块。与传统的静态导入(使用 import 关键字:import 命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行。)相比,动态导入可以根据需要按需加载模块,提供了更大的灵活性。

// 静态导入:语法错误(import 和 export 命令只能在模块的顶层,不能在代码块之中)
if (x === 2) {
	import MyModual from './myModual'
}

import 函数的参数 specifier,指定所要加载的模块的位置。import 命令能够接受什么参数,import() 函数就能接受什么参数,两者区别主要是后者为动态加载。

// 动态导入
import(specifier)

import() 返回一个 Promise 对象,因此可以使用 async/await 等方式来处理导入的模块。

适用场合

(1)按需加载:import() 可以在需要的时候,再加载某个模块。

// 只有用户点击了按钮,才会加载 dialogBox 模块
button.addEventListener('click', event => {
	import('./dialogBox.js').then(dialogBox => {
		dialogBox.open()
	}).catch(error => {
		/* Error handling */
	})
})

(2)条件加载:import() 可以放在 if 代码块,根据不同的情况,加载不同的模块。

if (condition) {
	import('moduleA').then(...)
} else {
	import('moduleB').then(...)
}

(3)动态的模块路径:import() 允许模块路径动态生成。

import(f()).then(...)

动态导入的优点

可以根据需要按需加载模块,避免一次性加载所有模块,提高性能和加载速度。

可以在条件满足时才加载模块,实现延迟加载和懒加载的效果。

可以动态地根据运行时信息选择不同的模块进行加载。

三、ES6 Module 和 CommonJS 模块的区别

ES6 Module 是 JavaScript 的官方模块系统,而 CommonJS 是 Node.js 的模块系统。ESM 具有更强大的特性和更高的效率,适用于现代浏览器环境和某些前端工具链中。而 CJS 则适用于 Node.js 环境和一些较老的浏览器环境。然而,随着 Node.js 对 ESM 的支持不断改进,ESM 已经成为了通用的模块系统,并逐渐取代了 CJS 的使用。

1、语法差异

CommonJS 使用 module.exports = {} 导出一个模块对象,require(‘file_path’) 引入模块对象。

ES6 Module 使用 import 和 export 关键字来导入和导出模块。

2、加载方式

CommonJS 在运行时同步加载模块。它会先加载整个模块,然后再执行模块代码。

ES6 Module 在解析阶段进行静态分析,即在编译时确定模块依赖关系。它使用异步加载模块的方式,在运行时按需加载模块。

3、输出差异

CommonJS 模块中,module.exports 导出的是一个值的拷贝。当其他模块使用 require 导入该模块时,会得到这个拷贝的值,而不是原始值本身。因此,如果在被导入的模块内部对导出的值进行修改,不会影响到其他模块中已经导入的值。

// module.js
let count = 0

setTimeout(() => {
	count++
}, 1000)

module.exports = count

// main.js
const count = require('./module')

setTimeout(() => {
	console.log(count) // 输出: 0,因为count只是module.js导出的初始值的一个拷贝,不会变化
}, 2000)

ES6 Module 模块中,export 导出的是一个值的引用。当其他模块使用 import 导入该模块时,得到的是原始值的引用。这意味着,如果在被导入的模块内部对导出的值进行修改,会直接影响到其他导入该模块的地方,因为它们引用同一个原始值。

// module.js
export let count = 0

setTimeout(() => {
	count++
}, 1000)

// main.js
import { count } from './module'

setTimeout(() => {
  console.log(count) // 输出: 1,因为count是module.js导出的原始值的引用,会随着变化而变化
}, 2000)

需要注意的是,虽然 ES6 模块的输出是引用,但并不意味着你可以直接修改导入的值。在严格模式下,对于 import 导入的值是只读的,不能直接修改。如果你想要修改导入的值,可以通过修改原始值或者使用 export default 来实现。

4、导入和导出特性

CommonJS 只支持默认导出,通过给 module.exports 赋值来导出模块。

ES6 Module 支持命名导出(Named Exports)和默认导出(Default Exports)两种导出方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/290101.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FCN学习-----第一课

语义分割中的全卷积网络 CVPR IEEE国际计算机视觉与模式识别会议 PAMI IEEE模式分析与机器智能汇刊 需要会的知识点: 神经网络:前向传播和反向传播 卷积神经网络:CNN,卷积,池化,上采样 分类网络&#xff1a…

CCF模拟题 202312-1 仓库规划

问题描述 试题编号: 202312-1 试题名称: 仓库规划 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 输入格式 输出格式 样例输入 4 2 0 0 -1 -1 1 2 0 -1样例输出 3 1 0 3样例解释 Java实现代码: import …

Spring高手之路-Spring Bean、Java Bean和对象的区别与联系

目录 什么是Spring Bean 什么是Java Bean 什么是对象 Spring Bean与Java Bean与对象的联系与区别 联系 区别 什么是Spring Bean 在Spring官方文档中对Bean的解释如下: In Spring, the objects that form the backbone of your application and that are manage…

贪吃蛇C语言实现(有源码)

前言 之前学了一点easyx图形库的使用&#xff0c;掌握一些基本用法后就用贪吃蛇来进行实战了&#xff0c;运行视频放在csdn视频那一栏了&#xff0c;之前的烟花也是。 1.头文件 #define _CRT_SECURE_NO_WARNINGS 1 #include<easyx.h> #include<conio.h> #includ…

odoo17 | 基本视图

前言 我们在上一章中已经看到Odoo能够为给定模型生成默认视图。在实践中&#xff0c;默认视图是绝对不可接受的用于商业应用程序。相反&#xff0c;我们至少应该以逻辑方式组织各种字段。 视图在带有动作和菜单的XML文件中定义。它们是ir.ui.view模型的实例。 在我们的房地产…

YOLO+SlowFast+DeepSORT 简单实现视频行为识别

前段时间刷短视频看到过别人用摄像头自动化监控员工上班状态&#xff0c;比如标注员工是不是离开了工位&#xff0c;在位置上是不是摸鱼。虽然是段子&#xff0c;但是这个是可以用识别技术实现一下&#xff0c;于是我在网上找&#xff0c;知道发现了 SlowFast&#xff0c;那么下…

Sharding-JDBC快速使用【笔记】

1 引言 最近在使用Sharding-JDBC实现项目中数据分片、读写分离需求&#xff0c;参考官方文档&#xff08;Sharding官方文档&#xff09;感觉内容庞杂不够有条理&#xff0c;重复内容比较多&#xff1b;现结合项目应用整理笔记如下供大家参考和自己回忆使用&#xff1b; 在…

苹果手机数据删除怎么恢复?这几个方法值得一试!

不小心删除了iPhone里的照片&#xff1f;别担心&#xff0c;数据恢复是有可能的&#xff01; 从这里&#xff0c;你可以找到你的备份并恢复丢失的数据。如果你没有备份&#xff0c;那么数据恢复软件可能可以帮助你。它们可以扫描你的iPhone或iTunes备份&#xff0c;找到你删除…

打字练习(Python代码模拟打字练习软件效果)

Python代码模拟打字练习软件效果&#xff0c;循环进行单行打字练习&#xff0c;结束时输出平均速度和综合正确率。 (笔记模板由python脚本于2024年01月03日 22:36:34创建&#xff0c;本篇笔记适合熟悉Python字符串和列表基本数据类型的coder翻阅) 【学习的细节是欢悦的历程】 P…

如何利用Oracle官方网站不登录账号下载和安装非最新版本的JDK(版本自由选择)

一、JDK概述 JDK&#xff08;Java Development Kit&#xff09;是Java开发工具集&#xff0c;是针对Java编程语言的软件开发环境。它包含了Java编译器、JRE&#xff08;Java运行时环境&#xff09;以及其他一些用于开发、调试和测试Java应用程序的工具&#xff0c;是Java开发人…

通用Mapper怎么开接口扫描

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

三维模型数据的几何坐标变换的点云重建并行计算技术方法分析

三维模型数据的几何坐标变换的点云重建并行计算技术方法分析 倾斜摄影三维模型数据的几何坐标变换与点云重建并行计算技术的探讨主要涉及以下几个方面&#xff1a; 1、坐标系定义与转换&#xff1a;在进行坐标变换前&#xff0c;需要确定各个参考系的定义并实现坐标系之间的转…

[Angular] 笔记 15:模板驱动表单 - 表单验证

油管视频&#xff1a; Form Validation 有三种类型的验证&#xff1a; valid, pristine(是否被编辑过&#xff0c;被改过)&#xff0c;以及 touched 相反的属性&#xff1a; invalid, dirty, untouched pokemon-template-form.component.html 代码修改&#xff1a; 任何时候…

geemap学习笔记040:GEE中样本点选择操作流程

前言 geemap中目前有一个bug&#xff0c;就是在选择样本点的时候不合理&#xff0c;选完一类样本之后&#xff0c;没法继续选择下一类&#xff0c;并且没法在线进行编辑和修改。因此目前就只能结合在线版的GEE进行样本选择&#xff0c;本节就详细的介绍一下GEE中样本点的选择过…

Transformer 架构解释

一、说明 变形金刚是机器学习的一个新发展&#xff0c;最近引起了很大的轰动。他们非常善于跟踪上下文&#xff0c;这就是为什么他们写的文本有意义。在本章中&#xff0c;我们将介绍它们的体系结构以及它们的工作原理。 amanatulla1606 Transformer 模型是机器学习中最令人兴奋…

机器学习期末复习

机器学习 选择题名词解释&#xff1a;简答题计算题一、线性回归二、决策树三、贝叶斯 选择题 机器学习利用经验 &#xff0c;须对以下&#xff08;&#xff09;进行分析 A 天气 B 数据 C 生活 D 语言 归纳偏好值指机器学习算法在学习的过程中&#xff0c;对以下&#xff08;&a…

酷开系统小酷少儿重磅升级!陪伴孩子美好童年!

孩子的成长总是匆匆太匆匆&#xff0c;父母们应该放慢脚步&#xff0c;感悟童心。用心灵和智慧陪伴孩子&#xff0c;在孩子的心中没有什么比幸福的家庭更重要&#xff0c;没有什么比父母的陪伴更美好&#xff01;酷开系统少儿频道全面升级&#xff01;让酷开系统小酷少儿陪伴成…

设计模式篇章(1)——理论基础

设计模式&#xff1a;在软件开发中会面临许多不断重复发生的问题&#xff0c;这些问题可能是代码冗余、反复修改旧代码、重写以前的代码、在旧代码上不断堆新的代码&#xff08;俗称屎山&#xff09;等难以扩展、不好维护的问题。因此1990年有四位大佬&#xff08;GoF组合&…

ant design pro 5 企业级后台前端框架自定义根路径设置,解决public文件夹下资源在打包部署后出现找不到的问题

关于ant design pro v5的开箱使用方法见&#xff1a;开箱即用的企业级数据和业务管理中后台前端框架Ant Design Pro 5的开箱使用和偏好配置-CSDN博客 在开发过程中为了方便我们可能会将部分静态资源如logo等放入public文件夹&#xff0c;但在设置站点根路径后&#xff0c;publi…

5.云原生安全之ingress配置域名TLS证书

文章目录 cloudflare配置使用cloudflare托管域名获取cloudflare API Token在cloudflare中配置SSL/TLS kubesphere使用cert-manager申请cloudflare证书安装证书管理器创建Secret资源创建cluster-issuer.yaml创建cert.yaml申请证书已经查看申请状态 部署harbor并配置ingress使用证…