CJS与ESM:CJS

模块化方案

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

如果我们在一个html文件中,引入脚本,以前是通过script脚本来引入:
在这里插入图片描述
这种方式下引入的脚本实际上相当于把所有的代码都放在了全局作用域中,假如a.js和b.js文件中都有name变量,首先会覆盖,其次是let,多次声明同一个变量会报错
在这里插入图片描述
这就是相当于在全局作用域中 重复声明与赋值

另外也可以通过立即执行函数将变量的作用域限制在函数内部,然后通过return 返回出去,这个就不多说了,重点说下cjs与esm

CommonJS:
CJS主要用于服务器端文件的导入和导出,我们在一个文件中可以定义变量和函数等,通过module.exports导出,module.exports是一个空对象,给这个对象身上添加属性,或者重新为其赋值为另一个对象,就会将其指向的对象作为导出对象导出出去

// 1.js
console.log(module.exports); //输出 {}
// 1.js
console.log(module.exports);let name = "aa";
let age = 20;
function sayHi() {console.log("hihihi");
}// 给对象添加属性
module.exports.name = name;
module.exports.age = age;
module.exports.sayHi = sayHi;console.log(module.exports);
//{ name: 'aa', age: 20, sayHi: [Function: sayHi] }

或者是让module.exports重新指向一个对象:

// 1.js
console.log(module.exports);let name = "aa";
let age = 20;
function sayHi() {console.log("hihihi");
}module.exports = {name,age,sayHi,
};console.log(module.exports);
//{ name: 'aa', age: 20, sayHi: [Function: sayHi] }

在另一个文件中通过require()导入:

// 1.js 导出
let name = "aa";
let age = 20;
function sayHi() {console.log("hihihi");
}module.exports = {name,age,sayHi,
};// 2.js 导入
const m1 = require("./1.js");
console.log(m1); 
//{ name: 'aa', age: 20, sayHi: [Function: sayHi] }

在CJS中,require(“./1.js”) 获取的实际上是,导出对象的地址,就是说,导出对象module.exports和m1变量中保存的地址是一样的,

导出的是原始类型时

假如导出的一个原始类型,即:

// 1.js 导出
let name = "aa";
let age = 20;module.exports = name// 2.js 导入
const m1 = require("./1.js");
console.log(m1); //aa

在导入的时候类似于原始类型变量的赋值,在2.js文件中创建了一个变量m1,然后拷贝了1.js中的name变量。
这时候,导出,导入值的改变不会对彼此产生影响,就类似于原始类型数据之间的拷贝,是独立的,互不影响。
举例来说:

// 1.js 导出
let name = "aa";// 2s以后修改name
setTimeout(() => {console.log("2s后在导出文件进行修改");name = "hahaha";
}, 2000);module.exports = name;// 2.js 导入
const m1 = require("./1.js");
console.log(m1);// 4s以后输出m1,看下导出文件1.js的修改,有没有对这里产生影响
setTimeout(() => {console.log('导入文件中没有影响',m1);
}, 4000);

输出

aa
2s后在导出文件进行修改
导入文件中没有影响 aa
导出的是对象类型时

假设导出的对象是一个对象类型,对象内部的属性有原始类型,也有复杂类型时。

// 1.js 导出
let name = "aa";
let age = 20;
let friends = {name: "zs",age: 22,score: [10, 20, 30],
};module.exports = {name,age,friends,
};//修改
setTimeout(() => {module.exports.name = "jjj";module.exports.friends.age = 100;
}, 2000);// 2.js 导入
const m1 = require("./1.js");
console.log(m1);setTimeout(() => {console.log(m1);
}, 3000);

输出

{name: 'aa',age: 20,friends: { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
}
{name: 'jjj',age: 20,friends: { name: 'zs', age: 100, score: [ 10, 20, 30 ] }
}

在1.js中,通过module.exports.的形式修改导出的数据时,可以发现导入文件中的数据也改变了,这是因为module.exports和m1,指向的内部地址是一样的,类似于对象的浅拷贝,相互之间会产生影响。

同样,如果在导入文件中修改导出的对象,导出文件中也会受到影响

// 1.js 导出
let name = "aa";
let age = 20;
let friends = {name: "zs",age: 22,score: [10, 20, 30],
};module.exports = {name,age,friends,
};setTimeout(() => {console.log("导出文件中的源导出数据", module.exports);
}, 3000);// 2.js 导入
const m1 = require("./1.js");
console.log("初始导入对象", m1);setTimeout(() => {console.log("导入文件中修改导出对象-----");m1.name = "2.js";console.log("修改完成");
}, 2000);

输出

初始导入对象 {name: 'aa',age: 20,friends: { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
}
导入文件中修改导出对象-----
修改完成
导出文件中的源导出数据 {name: '2.js',age: 20,friends: { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
}
导入的时候使用解构赋值

在导入文件的时候,我们直接解构对象:

const { name, age, friends } = require("./1.js");
console.log("初始导入对象", name, age, friends);

现在的代码含义是,导入1.js中的导出对象,同时我们使用了对象解构,在2.js中也定义了name,age,friends变量,然后他们被赋值为了导出对象中的对应值。
这时候就类似于进行浅拷贝

由于name,age都是原始类型,直接会拷贝一份,如果去修改导出文件的值,导入文件不受影响

// 1.js 导出
let name = "aa";
let age = 20;
let friends = {name: "zs",age: 22,score: [10, 20, 30],
};module.exports = {name,age,friends,
};setTimeout(() => {module.exports.name = "haha";console.log(module.exports);
}, 2000);// 2.js 导入
const { name, age, friends } = require("./1.js");
console.log("初始导入对象", name, age, friends);setTimeout(() => {console.log(name);
}, 3000);

输出:

初始导入对象 aa 20 { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
{name: 'haha',age: 20,friends: { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
}
aa

而friends是对象类型,如果我们在2.js中修改,也会反映到1.js中:

// 1.js 导出
let name = "aa";
let age = 20;
let friends = {name: "zs",age: 22,score: [10, 20, 30],
};module.exports = {name,age,friends,
};setTimeout(() => {console.log("导出文件中的源导出数据", module.exports);
}, 3000);// 2.js 导入
const { name, age, friends } = require("./1.js");
console.log("初始导入对象", name, age, friends);setTimeout(() => {console.log("导入文件中修改导出对象-----");friends.age = -1;console.log("修改完成");
}, 2000);

输出

初始导入对象 aa 20 { name: 'zs', age: 22, score: [ 10, 20, 30 ] }
导入文件中修改导出对象-----
修改完成
导出文件中的源导出数据 {name: 'aa',age: 20,friends: { name: 'zs', age: -1, score: [ 10, 20, 30 ] }
}

这是因为friends和module.exports.friends实际上指向的地址是一样的。

总结一下,实际上通过require()导入对象时,会执行一次文件,然后返回的是导出对象值的拷贝,如果是基本类型,就是复制了一份,如果是对象类型,就是返回了导出对象module.exports的内存地址。

整体给我的感觉就类似于浅拷贝。

commonjs是在代码运行时加载,因为commonjs是导出的整个对象,需要在脚本运行完成后才能生成
commonjs使用的是同步加载模块方式,require()的文件需要执行一遍。当文件都在服务器端时,就类型于文件都在我们本地,读取和执行都很快。
如果说是浏览器端,浏览器需要向服务器请求这些js文件,还需要下载文件,然后去执行文件,如果文件中引入了其他文件,又需要下载执行,这就会很耗费时间 ,所以在浏览器环境下我们不适用这种模块化方案,采用的是ESModule.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PEFT LoRA 介绍(LoRA微调使用的参数及方法)

一 PEFT LoRA 介绍 官网简介如下图: 翻译过来是:低秩自适应(LoRA)是一种PEFT方法,它将一个大矩阵在注意层分解成两个较小的低秩矩阵。这大大减少了需要微调的参数数量。 说的只是针对注意力层,其实我自己平时微调操作注意力层多…

nacos2.x作为配置中心和服务注册和发现以及springcloud使用

目录 一、nacos是什么 二、windows下安装配置nacos 1、准备 2、安装nacos 3、配置nacos 4、启动并且访问nacos 三、springcloud使用nacos作为配置中心 四、springcloud使用nacos进行服务注册与发现 五、springcloud使用nacos进行服务消费 六、nacos的一些高级配置 1…

Ubuntu上编译多个版本的frida

准备工作 Ubuntu20(WSL) 略 安装依赖 sudo apt update sudo apt-get install build-essential git lib32stdc-9-dev libc6-dev-i386 -y nodejs 去官网[1]下载nodejs,版本的话我就选的20.15.1: tar -xf node-v20.15.1-linux-x64.tar.xz 下载源码 …

AbutionGraph时序(流式)图数据库开发文档地址

AbutionGraph-时序(流式)图数据库,官方开发文档(API)地址: http://www.thutmose.cn

JavaSE从零开始到精通(九) - 双列集合

1.前言 Java 中的双列集合主要指的是可以存储键值对的集合类型,其中最常用的包括 Map 接口及其实现类。这些集合允许你以键值对的形式存储和管理数据,提供了便捷的按键访问值的方式。 2. HashMap HashMap 是基于哈希表实现的 Map 接口的类&#xff0c…

java算法day23

java算法day23 121买卖股票的最佳时机55 跳跃游戏45 跳跃游戏Ⅱ763划分子母区间 121买卖股票的最佳时机 最容易想的应该就是两个for暴力枚举。但是超时 本题用贪心做应该是最快的。 先看清楚题,题目要求在某一天买入,然后在某一天卖出,要求…

MarkTool集合篇

MarkTool目前包含以下几种工具 1、TCP客户端 2、TCP服务端 3、UDP客户端 4、Web客户端 5、Web服务端 6、串口网口 7、PLC 8、获取本机设备 9、Log 10、密钥 11、系统设置 11-1、基本设置 11-2、角色设置 11-3、用户设置 11-4、log记录 开启软件需要找我解密&#…

S7-1200PLC通过111报文和EPOS模式实现位置轴轴控功能(FB284封装)

EASY_SINA_POS的详细使用介绍请参考下面文章链接: S7-1200PLC使用标准报文111和EPOS模式实现V90 PN总线伺服定位(Easy_SINA_Pos)_西门子sinapos-CSDN博客文章浏览阅读132次。文章浏览阅读7k次。先简单说下如何获取FB284,一般有2种方法,Startdrive软件可以操作大部分西门子的…

PostgreSQL使用(四)——数据查询

说明:对于一门SQL语言,数据查询是我们非常常用的,也是SQL语言中非常大的一块。本文介绍PostgreSQL使用中的数据查询,如有一张表,内容如下: 简单查询 --- 1.查询某张表的全部数据 select * from tb_student…

【Qt】QLabel常用属性相关API

QLabel是Qt框架中用于显示文本或图案的小部件。在Qt应用程序中,QLabel是用来呈现静态文本或图像给用户的重要部分 QLabel属性陈列 属性说明textQLabel中的文本内容textFormat 文本的格式 Qt::PlainText 纯文本Qt::RichText 富文本Qt::MarkdownText markdown…

【数据结构】排序算法(冒泡排序、插入排序、希尔排序、选择排序、堆排序、计数排序)

生命不可能有两次,但许多人连一次也不善于度过。💓💓💓 目录 ✨说在前面 🍋知识点一:排序的概念和应用 • 🌰1.排序及其概念 • 🌰2.排序的应用 • 🌰3.常见的排序算…

qt做的分页控件

介绍 qt做的分页控件 如何使用 创建 Pagination必须基于一个QWidget创建,否则会引发错误。 Pagination* pa new Pagination(QWidget*);设置总页数 Pagination需要设置一个总的页数,来初始化页码。 pa->SetTotalItem(count);设置可选的每页数量…

前端养成记-实现一个低配版简单版本的vue3表单自定义设计组件

简介: 通过使用了最新的vue3,vite2,TypeScript等主流技术开发,并配合使用vuedraggable 插件以及antd design vue 组件库实现低配版本的自定义表单设计组件; 项目地址:https://gitee.com/hejunqing/vue3-antdv-generator

PyTorch模型训练步步详解:从零开始构建深度学习流程

P y T o r c h 训练模型流程图 PyTorch训练模型流程图 P y T orc h 训练模型流程图

非凸T0算法,如何获取超额收益?

什么是非凸 T0 算法? 非凸 T0 算法基于投资者持有的股票持仓,利用机器学习等技术,短周期预测,全自动操作,抓取行情波动价差,增厚产品收益。通过开仓金额限制、持仓时长控制等,把控盈亏风险&…

学习笔记:MySQL数据库操作5

1. 触发器(Triggers) 触发器是数据库的一种高级功能,它允许在执行特定数据库操作(如INSERT、UPDATE、DELETE)之前或之后自动执行一段代码。 1.1 创建商品和订单表 商品表(goods) gid: 商品编号…

会话存储、本地存储,路由导航守卫、web会话跟踪、JWT生成token、axios请求拦截、响应拦截

1、会话存储、本地存储 前端浏览器中存储用户信息,会话存储、本地存储、cookie 会话存储(sessionStorage):会话期间存储,关闭浏览器后,数据就会销毁 sessionStorage.setItem("account",resp.d…

反射型与dom型的xss的区别【源码分析】

反射型 XSS 和 DOM 型 XSS 都属于跨站脚本攻击 (XSS) 的类型,它们的共同点是均能通过注入恶意脚本在用户浏览器中执行,不同点是dom型xss不经过服务器,而反射型是经过服务器的。但是,它们在攻击方式、执行过程和防御措施上有所不同…

Servlet2-HTTP协议、HttpServletRequest类、HttpServletResponse类

目录 HTTP协议 什么是HTTP协议 HTTP协议的特点 请求的HTTP协议格式 GET请求 POST请求 常用的请求头说明 哪些是GET请求,哪些是POST请求 响应的HTTP协议格式 常见的响应码说明 MIME类型说明 HttpServletRequest类 作用 常用方法 如何获取请求参数 po…

IP 泄露: 原因与避免方法

始终关注您的IP信息! 您的IP地址不仅显示您的位置,它包含几乎所有的互联网活动信息! 如果出现IP泄漏,几乎所有的信息都会被捕获甚至非法利用! 那么,网站究竟如何追踪您的IP地址?您又如何有效…